Jump to content


Popular Content

Showing content with the highest reputation on 07/16/2013 in all areas

  1. 9 points
    This tutorial will outline how to create a membership system that requires account activation via email. If you have a decent understanding of ProcessWire it isn't difficult at all! Create additional fields By default ProcessWire has a name, email, and password field for the user template. To allow for account activation we will have to add a couple more fields. Create the following fields and add them to the systems default user template. You can make ProcessWire show the system templates by going to setup -> templates -> toggle the filter tab -> show system templates -> yes. user_real_name user_activation Okay great, now that you have added the two additional fields to the user template we can start to code the registration form. I am not going to spend a lot of time on this part, because there is a tutorial that describes creating forms via the API. Create the registration form Once you have followed the tutorial on creating forms, you will have to add a couple of sections to your new form! <?php include("./functions.php"); require("/phpmailer/class.phpmailer.php"); $out = ""; $errors = ""; //create form //full name field //email field //username field //password field //submit button //form submitted if($input->post->submit) { $form->processInput($input->post); //instantiate variables taking in the form data $full_name = $form->get("full-name")->value; .... /* * Create the activation code * You can add a random string onto the * the username variable to keep people * from cracking the hash * ex $activation = md5($username."processwire"); */ $activation = md5($username); $activation_code = $config->httpHost."/activation/?user=".$username."&hash=".$activation; //check for errors if($form->getErrors() || username_validation($username) == 0) { $out .= $form->render(); //process errors /* * this checks to makesure that no one has the username * I have a functions file that I import to the pages I * need it on */ if(strlen($username) != 0){ if(username_validation($username) == 0) { $username->error = " "; $errors .= "Sorry that username is already taken!"; } } } //the registration was successful else { $out = "Thank you for registering!<br><br>"; $out .= "An email has just been sent to " . $email . " with the url to activate your account"; /* * In this example I am using phpmailer to send the email * I prefer this, but you can also use the mail() function */ $mail = new PHPMailer(); $mail->IsHTML(true); $mail->From = "email@domain.com"; $mail->FromName = "Register @ Your Site"; $mail->AddAddress($email); $mail->AddReplyTo("email@domain.com","Register @ Your Site"); $mail->Subject = "Registration"; $mail->Body = " Hi " . $full_name. ", <br>" . "Thanks for registering at Your Site. To activate your email address click the link below! <br><br>" . "Activation Link: <a href='http://".$activation_code."'>".$activation_code."</a>"; $mail->send(); //create the new user $new_user = new User(); $new_user->of(false); $new_user->name = $username; $new_user->email = $email; $new_user->pass = $password; $new_user->addRole("guest"); $new_user->user_full_name = $full_name; $new_user->user_activation = $activation; $new_user->save(); $new_user->of(true); } } //form not submitted else { $out .= $form->render(); } ?> <h2>REGISTER</h2> <div class="errors"><?php echo $errors; ?></div> <?php echo $out; ?> Okay so that outlines the entire form. Let me get into the important parts. Checking for a unique username /* * check if username exists * return 1 username is valid * return 0 username is taken */ function username_validation($username) { $valid = 1; $check = wire("users")->get($username); if ($check->id) { $valid = 0; } return $valid; } We don't want to try and add a username if the username is already taken, so we need to make sure to validate it. If this returns 0 you should output that the username is already taken, and the user needs to choose a different one. Generating an activation code /* * Create the activation code */ $activation = md5($username); $activation_code = $config->httpHost."/activation/?user=".$username."&hash=".$activation; This generates an activation code. It does so by encrypting the username variable and then combines the username and activation code into a url for a user to visit. Now we have to process the activation code. As the teppo recommended, it is a good idea to add an extra string onto the $username when encrypting it with md5. If you don't do this, people may crack it and allow for mass signups. $activation = md5($username."Cech4tHe"); Activate the user <?php include("./head.inc"); include("./functions.php"); /* * this will pull the username and * activation code from the url * it is extremely important to * clean the string */ $activate_username = $sanitizer->text($_GET['user']); $activate_hash = $sanitizer->text($_GET['hash']); if(wire("users")->get($activate_username)->id) { if(strcmp(wire("users")->get($activate_username)->user_activation, $activate_hash) == 0 || wire("users")->get($activate_username)->user_activation == 0) { echo "Your account has been activated!<br><br>"; $activate_user = wire("users")->get($activate_username); $activate_user->of(false); $activate_user->user_activation = "0"; $activate_user->save(); } else { echo "There was an error activating your account! Please contact us!<br><br>"; } } else { echo "Sorry, but that we couldn't find your account in our database!<br><br>"; } include("./foot.inc"); This pulls the username and activation hash from the url. It then goes into the database and looks for the user. Once it finds the user, it get's the user_activation hash and compares it to the one in the URL. If it matches, it activates the user by setting user_activation to zero. Clean the url You can user the built in $sanitizer->text() method to clean the url. This will keep people from inserting special characters that can break the page. Keeping unactivated users out You aren't done yet! Now you will have to choose which areas you want to restrict to activated users only. An activated user's user_activation field will now equal zero. So if you want to restrict a section check to make sure it is zero...... if($user->user_activation != 0) { echo "Sorry, but you need to activate your account!"; } else { // activated users }
  2. 5 points
    TextformatterImageInterceptor ( Textformatter module ) Let editors use WYSIWYG images, but let you control the image size, aspect ratio & behaviour. How to install Copy the TextformatterImageInterceptor.module file to your /site/modules/ directory (or place it in /site/modules/TextformatterImageInterceptor/). Click check for new modules in ProcessWire Admin Modules screen. Click install for the module labeled: "Image Interceptor". Go to the module config screen and set the settings you wish. How to use Edit your body field in Setup > Fields (or whatever field(s) you will be placing controlled images in). On the details tab, find the Text Formatters field and select "Image Interceptor". Save.About the settings Render Inline styles If checked, some inline styles are added to the image.High Density Double the pixel width of an image if the percentage is not set. (fixed image size)Default there are settings for landscape & portrait images. Squared images will inherit all settings belonging to portrait settings and there's a way to escape the default settings. But before we dive deeper in tagged sets I want to spread some light on the landscape/portrait settings. All images portrait/landscape wil get the class name .default. ps, All images including tagged set images get the image orientation as class (.landscape / .portrtait) Percentage The width of the image in percentage. This setting makes the image responsive or if left blank the image wil be fixed size. Images receive a .responsive and a .p-50 class (where 50 is the width in percentage) Width The width of the image in pixels. So the width of the image in percentage and the pixel width combined wil be the key to pixel desity. Alignment There are 5 different ways to align an image. left, center, right, inherit (inherits from the WYSIWYG editor) and not aligned. If render inline styles is checked the aligment wil be set directly in the inline style of the image. Alignment classes wil be added to the image. Aspect Ratio (cropping) if an aspect ratio is given, the image will be cropped to the given ratio. If you type 2:1 in the landscape settings. Images 800 pixels wide, will be croped to a 800x400 image. The image gets the following classes: .cropped and .crop-2x1 Image Captions Type here your class name(s) for the caption. When a class name is provided and an image has a description, the image is wrapped (if not already) and *has-* is set in front of the class name(s). For the caption a div is created with the class name(s) and the image description as text. Next to these settings. You can give custom classes to images. This way you can give it framework specific classes for example. And you're allowed to wrap the images with custom HTML. (Some frameworks needs additional HTML to make images more fancy) Then additional styles could be added to images if render inline styles is checked. Tagged sets Tagged sets are an image tag followed by settings specific for images with that tag. To enable tagged sets, the image field need "Use Tags?" to be checked. Go to setup, then fields go to your image field and under the details tab check "Use Tags?". Taged sets are a good way to escape the default image behaviour. Say you have a bunch of nicely ordered images on the page, but you want to show a company logo on 150px floated left. With tagged sets it's no problem. type: (logo 150px left) on one line and you've created your first tagged set. (don't forget to tag the image to) If you want captions for a tagged set, keep in mind that captions need at least 1 class. The format to enter: caption.class-name. For an image wrapper we use the same rules. The only difference is we start typing wrapper followed by class names starting with a dot. example: wrapper.logo.stand-out. You can have a multitude of sets, every set on it's own line. Every set needs at least a tag-name and a pixel width. Note: If you use a wrapper or captions (wrapper will be created if none is set), the inline styles and specific width & alignment classes will be set to the wrapper and removed from the image. This way no duplication of styles wil take place. github modules directory
  3. 3 points
    All ProcessWire fields are indexed (including datetime fields), so I don't think you'll have a problem with it being slow. But if you want to retrieve and load a 1000+ pages at a time, that could be potentially slow (and perhaps run out of memory). It's important to place limits (i.e. "limit=50") on any queries that can return huge amounts of pages, in order to ensure that everything stays running fast. I don't think that would be slow. But paginate your results by placing a limit to be sure. Also, I doubt that you need the parent=/events/ since you are already specifying a template. Though if I understand your structure right, if you did want to specify a parent, you'd probably want to use has_parent rather than parent, since the time pages live under /events/some-event/ rather than /events/. Last thing to note is that you can't have spaces before/after operators like you do in your time.date_end > $today" -- that would need to be "time.date_end>today". Btw, you can specify "today" rather than populating a $today variable, if you prefer. Actually you can do this. But note the same suggestions I had above. There would also be this alternative, which might be potentially faster because ProcessWire is a little more optimized for parent-field matching queries than children-field matching queries (given that a page can have only 1 parent, but any number of children): $pages->find("template=time, parent.on_frontpage=1, enddate>today"); When you foreach the results of that, note they are 'time' pages rather than 'event' pages, so you'd want to refer to the parent in order to get the event.
  4. 2 points
    .. or combine above answers to get a method that instantiates and returns your submodule when/if needed: public function submodule() { return wire('modules')->get("myModule2"); } $modules->get("myModule")->submodule()->doSomething();
  5. 2 points
    $articles = $pages->find("parent=/news/, limit=18"); $out = ''; foreach ($articles as $key => $article) { if ($key == 0) $out .= "<div class='slide first'>"; // Open the first .slide else if ($key % 3 == 0) $out .= "</div><div class='slide'>"; // Close the previous slide and open next $out .= "<div class='post'><h2>$article->title</h2></div>"; // render the actual markup } $out .= "</div>"; // Close the final .slide echo $out; not tested and written in browser. You can omit the limit if you want to.
  6. 1 point
    Hi there, first the compliments: I found Processwire about a week ago (where have you been all this time PW?), and I found it mind blowing, basically because how easy is to create a site. I had my homepage made with Symphony CMS, and it was fun to develop with, but XSLT still gives me headaches (even I still have love for Symphony and its simple approach, and the power that XSLT gives). I've worked before with Drupal, Wordpress and some little experience with Joomla!, and always found myself programming hacks to make the CMS do exactly what I wanted. The I found Processwire and it took me less than three days to learn and migrate my homepage. And I love it. I'll buy the cache module even if I don't really need it, just for the support, Ryan's work here is simply amazing. So now I'm preparing myself to start to work with PW for future clients. So here is my question, maybe a dumb one, but I need advice on how to approach this: A client needs a frontend for managing some data stored in his database (separated from PW). They manage their own users and even an authentication system by their own from their portal. So probably the pages I will have to create within PW are the needed admin sections, and then make internally calls to their database. So here's where I need some advice, to make it the correct PW way: Should I use mysqli_query directly in the file template to obtain the data, or is there a method in the API to query external databases? Also I could create pages directly from the queries (let's say, I make a query returning 5 integer values, and then create these values automatically in pages, so I can have them directly in PW, but I'm not quite comfortable with that because redundancy). Also the authentification system, is there a way I could integrate their authentification? I was thinking on adding a request in the head.inc to see if the users is authenticated, and if not, redirect them accordinlgy. So there are the questions, sorry if they seem dumb (or with obvious answers), but I'm just diving into PW, and I want to do it right. Cheers
  7. 1 point
    Perfect solution. For those who find this and want to deal with it. In site/templates-admin/default.php, modify this line to read: $last_modified = $pages->find('limit=10, sort=-modified, start=0');
  8. 1 point
    You can define start=0 on where you don't want pagination.
  9. 1 point
    $this->submodule = wire("modules")->get("submodule"); ..Call in init(). Use in template. $m $modules->get("mainmodule"); $m->submodule->subfunction(); --- or creating a template var $this->fuel->set("submodule", wire("modules")->get("submodule")); ..call in init(). Use in template: $submodule->subfunction();
  10. 1 point
    How did you migrate, what features are disabled? ;-) Maybe you have some corrupt files, try to reupload them. Also try to clear /site/assets/cache/ directory
  11. 1 point
    @kyle: well written and easy to read, thanks for sharing! Two things I'd like to point out: You might want to add some random element to your activation hashes. MD5 is easy to spot and based on that information anyone mischievous enough could easily do things like automated account creation. Not that it would matter in most cases, but if a site using this was going to be in large-scale use then I'd be a bit worried about that. You're using custom methods to sanitize URLs, usernames, hashes etc. Take a look at $sanitizer -- it provides methods for sanitizing PW usernames, text (this should be a good fit for your hash) and even URLs. No need to reinvent the wheel
  12. 1 point
    Welcome @pnikosis, glad that you are enjoying ProcessWire! If you've got an external database that is still being updated than I would just query the external database rather than trying to load it into PW. On the other hand, if the database is no longer being updated (meaning, you wouldn't have to maintain two versions) then moving it into PW might make sense. But for querying the external database, you can use mysqli, but you might also want to look at switching to the PW dev branch (future version 2.4) which switches to the PDO database driver. Mysqli is still available in 2.4, so you can actually use whichever you prefer. Though mysqli will probably be dropped once we reach ProcessWire 3.0, but if it's installed in your PHP then of course it remains available for you. So if you use mysqli, probably it's better to use PHP's "mysqli" class rather than ProcessWire's extension of it, just for future compatibility.
  13. 1 point
    I dont understand what you dont get. You can sort the item with drag and drop on a page. See the picture above in this thread. See the icon on each item on the left?
  14. 1 point
    It sounds to me like an issue of case, somewhere. The skyscrapers profile field name is fieldset_meta_END (where END is uppercase). What is the case of the table in your MySQL db? I'm guessing it's got the uppercase END in it. You might try adjusting this setting in your /site/config.php to false, or rename the table in PhpMyAdmin to be all lowercase.
  15. 1 point
    I confess I didn't think of this when I created the module... Thanks for finding it I already corrected it on github, but if you don't want to download it again, just add these changes to the module file: static public function getModuleInfo() { return array( 'title' => 'Admin Custom Pages', 'summary' => 'Create custom admin pages without building a Process Module', 'version' => 105, // <-- change the version number 'author' => 'Diogo Oliveira', // <-- add this comma 'permission' => 'page-view' // <-- add this line ); } This gives permission to anyone to use the process, but you still can control the permissions for the pages in their templates. Edit: kongondo, he means the process itself. Even if the page is viewable by the user, the process wouldn't run, and throw that message.
  16. 1 point
    The timezone works correctly now - my obvious mistake, thanks for pointing it. It's like this: when someone sends data via the form (created in Form Builder) the data is then inserted into the DB. When I check time of the entry (lets skip the date to simplify things) in phpMyAdmin it shows me for example 13:30 - and everything is OK by now. But when I generate the data stored in DB as an output on the webpage the time of the entry is: 13:30 pm. To generate the date/time I use: $entry['created'] So my question was how can I make am/pm not to appear To get things more transparent here is the code for generating DB entries, plain and simple: <?php foreach($forms->get("wall-form")->entries->find() as $e) { echo "<div class='post'>"; echo "<h3>{$e['temat']}</h3>"; echo "<p><b>Dodane:</b> {$e['created']}</p>"; echo "<p><b>Autor:</b> {$e['autor']}</p>"; echo "<p><b>E-mail:</b> {$e['e_mail']}</p>"; echo "<p>{$e['wpis']}</p>"; echo "</div>"; } ?>
  17. 1 point
    On the __contruct(), the MailChimp Api class gets called looks like there's something broken there. The MCAPI.class.php should be loaded there. I strongly advise not to use this plug-in now. As it is standing still for months now. Recently I started to look at it again. ( Actually I'm busy with it now, building in Segmenting options ) My wish is that it will be workable soon, but time's not on my side. ( Next weeks gonna be very busy, vacation, doing nothing but drink beer & make some fun) I will post it here when it's in Beta...
  18. 1 point
    Fortunately... After reading and searching article on this site. This is working code. function bootwireCarousel(){ // We will need to treat the first image separately $slides = wire("pages")->get("/slideshow/")->children('template=slidefeat, limit=3,sort=-date, include=hidden'); $firstImage = wire("pages")->get("/slideshow/")->child; $out =" "; // The indicators $out .="<ol class='carousel-indicators'>"; // the carousel indicators need to start at 0 $count = -1; foreach($slides as $slide) { $activeClass = ($slide === $firstImage ? 'active' : ''); $count++; $out .="<li data-target='#myCarousel' data-slide-to='{$count}' class='{$activeClass}'></li>"; } $out .="</ol>"; // Carousel Items $out .="<div class='carousel-inner'>"; foreach($slides as $slide) { $activeClass = ($slide === $firstImage ? 'active' : ''); $out .="<div class='{$activeClass} item'>"; $out .="<img src='{$slide->images->url}{$slide->images}' />"; $out .="<div class='container'>"; $out .="<div class='carousel-caption'><h1>{$slide->title}</h1><p class='lead'>{$slide->body}</p></div>"; $out .="</div>"; // /container $out .="</div>"; } $out .="</div>"; // /carousel inner // Carousel optional nav - you can remove these if you dont want the side arrows $out .="<a class='carousel-control left' href='#myCarousel' data-slide='prev'>‹</a>"; $out .="<a class='carousel-control right' href='#myCarousel' data-slide='next'>›</a>"; if($firstImage) { echo "<section id='myCarousel' class='carousel slide'>{$out}</section>"; } } Thanks for you all...
  19. 1 point
    Greetings, No problem! Of course, there are also lots of JQuery plugins that will also work. I just prefer to use straight JS if I can. Let me know if there are other parts of the site I can describe. This project is being reviewed in presentations. I am planning to expand the project in response to feedback over the next few weeks. I will be building an "educators" section. Also, I will be creating a front-end interface for managing the site. I'll report back on all this soon. I also want to emphasize how wonderful it is to use ProcessWire for this kind of site. It is so natural to tie into good PHP/JS practices, I can give the client complete control of the system, and whenever necessary I can jump in to make improvements to the site with clean code. Thanks, Matthew
  20. 1 point
    @kyle... An example right of the docs to give you an idea..See line 9; it shows an example of adding 1 image. For several, you will want to foreach...there are several posts about this in the forums. Can't find them atm; you might want to search if you are still stuck $skyscraper = new Page(); $skyscraper->template = $templates->get("skyscraper"); $skyscraper->parent = $pages->get("/cities/atlanta/"); $skyscraper->title = "W-Hotel Tower"; $skyscraper->height = 400; $skyscraper->year = 2009; $skyscraper->body = "This is a nice hotel and it has a helicopter landing pad on top of it."; $skyscraper->save(); $skyscraper->images->add("http://starwoodhotels.com/w-atlanta.jpg"); $skyscraper->save();
  21. 1 point
    what field is called in admin ? lower cases all ? feld name matchamos table name ? and lower /uppers case ?
  22. 1 point
    I have it run!! I'm not sure I can follow you completely and I have a difficult time understanding what is wrong with your setup and how much you understand what you're doing. First, my setup is like this: We need a multiple select page field you can select from all page in your site tree, that is.. - pagelist_hidden - a page field, multiple page (page array) checked, the setting is "Home" page as the parent and the inputtype is PageListSelectMultiple* - No I attach this field to the user template (system template needs template filter set "Show system templates?" to show) So now I can select and add pages that should be hidden on every user page, since user template now has this field. Now if you login with the user, page are not shown in page tree anymore. This module does only this to filter those pages out on the ajax json response the admin page tree works with. It's more of a workaround and proof of concept / example module. It shows how it could be done. While this is simple, adding more features and configuration would go pretty complex and I'm not sure it's a route we should promote. Even just to reverse and add pages the user should see is a differnt thing considering a tree based structure. It's doable but maybe should be avoided. I your case it sounds like every time a new user is added you need to do a lot and add the new pages to each user. And you have templates for each user, but all the same setup? To get around the template based permissions I guess? I think it also could be avoided by using almost exact like module to add pages the user can edit and go with one set of templates. I think there's also some examples or even a module by Ryan around. https://github.com/ryancramerdesign/PageEditPerUser http://processwire.com/talk/topic/2141-module-page-edit-per-user/ http://processwire.com/talk/topic/3051-page-edit-per-user-and-template-access-how-do-they-relate/
  23. 1 point
    I only see that in the Admins Page-Tree: Start - Admin - Access - Users admin guest user1 ... When I there go to "user1" no template is displayed, there are only Tabs for content and delete. So I find nothing where to put the field pagelist_hidden. Under management/templates is no template therefor. Ups - sorry - now I understand "you have to filter for system templates" - looking again in templates, I thought ALL templates are listend. Ok, thanks, I try now...
  24. 1 point
    Ok, starting from the end. Put very roughly, $page represents a single page while $pages represents all pages. The methods from $page http://processwire.com/api/cheatsheet/#page can be applied to any object that represents a single page. $page, when used simply like this, represents the page that called the current template (usually by being viewed in the browser), but the $page methods can be applied to any Page object (other pages, even if they didn't call the current template). How do we get all those pages that are not represented directly by the variable $page? You guessed, using $pages http://processwire.com/api/cheatsheet/#pages $otherPage = $pages->get("/about/"); The get() method of $pages, returns always only one page. Meaning that $otherPage, is now an object equivalent to $page. Because of this, you can apply to it all the methods and call all properties of the object $page: echo $otherPage->id; While get() returns a single page, find() returns an array of pages, to be more precise a PageArray, and to those arrays you can apply any of the PageArray methods and call it's properties http://processwire.com/api/cheatsheet/#wirearray $pagesList = $pages->find("template=basic-page"); $pagesList->shuffle(); //same as: $pagesList = $pages->find("template=basic-page")->shuffle(); and because $pagesList is now a list with lots of $page equivalents inside it, you can go to each one individually and apply the same methods and call the same properties as with $page: foreach($pagesList as $individualPage){ echo $individualPage->id; } Disclaimers: I did an over simplification of the terms (same as in the cheatsheet). To make it clear: $page is a variable that holds a Page object that represents the current page. The methods I keep referring and that are on the cheatsheet, are in reality methods of Page objects, and not $page itself. The same applies to $pages and Pages. I must have some errors there because I'm far from a PHP specialist (even less concerning OOP). edit: Ryan, I saw that you answered before me, but I think to the first part. I will read now
  • Create New...