Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 09/01/2020 in all areas

  1. Back in 2005/6, I built a SSG/CMS combination application, based on a Perl Framework that I had written. The magazines' editors input their stories via a CMS login, and then, when they were done, they clicked a button that sent a special secure command to the server to run a script that generated all of the pages as static HTML files. It generated thousands of pages blazingly quickly. Having done that, I think ProcessWire with ProCache accomplishes the same thing, since it also writes HTML files. But... ProcessWire is SO much better than my application ever was, for so many reasons. ProcessWire with ProCache gives us the best of both worlds. Peter
    3 points
  2. if($user->hasRole('student') {
    2 points
  3. Hi, I added the 7G firewall htaccess rules to a site, and was getting errors in the PW admin as JS files were failing to load. The culprit is line 85 (v1.2): RewriteCond %{REQUEST_URI} (/)((php|web)?shell|conf(ig)?|crossdomain|fileditor|locus7|nstview|php(get|remoteview|writer)|r57|remview|sshphp|storm7|webadmin)(.*)(\.|\() [NC,OR] It is the conf(ig) part that prevents some PW admin JS files from being loaded (e.g. /wire/modules/AdminTheme/AdminThemeUikit/config-field.js). I removed this so the line becomes: RewriteCond %{REQUEST_URI} (/)((php|web)?shell|crossdomain|fileditor|locus7|nstview|php(get|remoteview|writer)|r57|remview|sshphp|storm7|webadmin)(.*)(\.|\() [NC,OR] Hopefully this will same some future users of the 7G firewall some time! If you come across any other issues using 7G with PW, please post them here! Cheers, Chris
    2 points
  4. 25,000 per hour/day/week? It all depends. From what you described about the app, even 25k users a day should be fine to start with a VPS with 2GB of RAM. If its usage increase a lot you can move the database to another VPS on its own AND/OR increase the VPS CPU and RAM resources very quick. My advice is to keep things simple and not try to over-engineer the application performance from the start. But if you feel "adventurous" ? or think that the project will have this many users right away on day one (which I doubt) you can start with a "serverless" approach where the infrastructure is scaled on-demand, using https://vapor.laravel.com/ and you don't have to worry. There's a course about this approach that is very interesting: https://serverlesslaravelcourse.com/
    2 points
  5. I'm working on a script for importing very old static HTML files into ProcessWire so they are searchable on the new site. What I have so far works, but I wonder if there are ways I can make the work of cleaning up the imported content easier, by doing more useful cleanup during the import. For this demo, suppose all the files exist in one directory, called "public", and suppose we're importing them all into the basic-page template. At this point, the basic-page template has been modified from the blank profile to include one additional textarea field called "body", which uses the CKEditor. <?php include './path/to/processwire/index.php'; // Use FileSystemIterator to save all the files in the 'public' directory // https://www.php.net/manual/en/class.filesystemiterator.php $files = new FileSystemIterator('./public'); // This is a callback function for the CallbackFilterIterator below $is_html_file = function($file) { return strpos($file->getFilename(), '.htm'); }; // Use CallbackFilterIterator to winnow the files down to only HTML files // https://www.php.net/manual/en/class.callbackfilteriterator.php $html_files = new CallbackFilterIterator($files, $is_html_file); // Input a regular expression and a string -> output an array of matches $preg_matches = function($regex, $string) { preg_match($regex, $string, $array); return $array; }; // Iterate over the directory objects stored in $html_files foreach($html_files as $file) { // Turn this file into a SplFileObject so we can read its contents // https://www.php.net/manual/en/class.splfileobject.php#splfileobject.constants.drop-new-line $_file = new SplFileObject($file); $contents = $_file->fread($_file->getSize()); $h1_content = $preg_matches('/\<h1\>(.*?)\<\/h1\>/i', $contents)[1] | false; // Create a new ProcessWire page and save the content into it $article = new \ProcessWire\Page(); $article->parent = $pages->get('/'); $article->template = 'basic-page'; $article->title = preg_match('/\<h1\>(.*?)\<\/h1\>/i', $contents) ? $preg_matches('/\<h1\>(.*?)\<\/h1\>/i', $contents)[1] : $preg_matches('/\<title\>(.*?)\<\/title\>/i', $contents)[1]; $article->body = $contents; $article->save(); } This successfully titles all the pages that have at least one h1 tag. (I know this is making a big assumption of proper markup, but it appears to be broadly correct in this one case.) The rest of the content is dumped into the page's body field. If this helps anyone else solve a similar problem, have the code! (WTFPL) But when one is dealing with archaic HTML using font tags and tables for layout (yeek!), this leaves much room for improvement. Something I'd like to do is get rid of all the layout tables and site furniture, like branding markup, navigation, and footer text. Of course, that is not marked up in a consistent way across all the documents. ? I wonder if anyone has guidance for something like this? Do you know of any best practices for automating the cleanup old HTML? Thank you! Edit: When searching for HTML tags, matches should be case insensitive (using the i flag after the delimiter). Also, use the content of the title element when there is no h1 tag on the page. This is all fixed in the code above.
    1 point
  6. Not to hijack this thread, but recently I experienced something like this. ProcessWire said I had two identical modules but I didn't! A couple of refreshes sorted it out, so I didn't investigate it further.
    1 point
  7. Hi, first of all, make a backup of everything. About the issue, it look like you have 2 modules file for the Fieldtype Repeater. One in wire/modules and another one in site/modules. Did you modified intentionally the module and made a copy of it in your site/module dir ? If yes, then do a Modules > Refresh then select the right Repeater module you want. If NOT, then rename the site/modules/Fieldtype/FieldtypeRepeater/ to site/modules/Fieldtype/.FieldtypeRepeater/ (note the dot before the module name) and do a Modules > Refresh. (it's quite strange, maybe a bad upload moment from you.)
    1 point
  8. If you have highly variable load I'd suggest going with something like heroku for manual scaling (if planable) or even solutions, which can automatically scale. No need to run a super beefy server for a whole month if only a single day has actual load. 25k/hour is around 7req/s (likely higher for varying load over the duration of the hour), so either requests must be served in <1/7s or you need more server instances.
    1 point
  9. Hey @johnstephens when I'm running into such problems, I'm trying to figure out which variable has which value at which time. A tool which is very helpful is TracyDebugger. https://modules.processwire.com/modules/tracy-debugger/ When you have installed the module you could do something like this... $dom = new \DOMDocument(); bd($dom); @$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')); bd($dom->getElementsByTagName('img')); foreach($dom->getElementsByTagName('img') as $img) { bd(img); } and you'll see exactly what you need to know - https://adrianbj.github.io/TracyDebugger/#/debug-bar?id=dumps Just give it a try and you'll love it ?
    1 point
  10. It's definitely possible. I built a complicated online scholarship application with ProcessWire, while not the same thing as an exam, it did have some similarities. An application would be initiated by a nominator on behalf of the person being nominated, and then there were 3 reference people per application (it got pretty complicated). Notification emails, application statuses, a bunch of other things. In my case, the public interface was mostly the in admin, so it had no "frontend" (other than some templates that would be used for redirecting to the edit url). That meant using roles, permissions and hooks accordingly to lock things down. In your use case, I would say to build a custom front-end for it and not do it the way I did it. It will be easier and I think it makes more sense for your use case (ie, multiple choice questions as opposed to all kinds of various fields from files, rich text, etc.). One crucial thing to test heavily is making sure the test takers don't get logged out for some reason. In my situation, that happened and I could not exactly pinpoint why, but it caused a lot of trouble especially since the application involved people filling out long-form answers, which would then be wiped when they hit the save button and their session had expired. I'll have to figure that out in the next iteration, but that was bad. You may want to have one question per page. This is how online / computer-based tests usually do it anyway. As for structure, maybe this could work: Home Exams Exam 1 Question 1 ('question' template) Answer Option 1 ('answer' template) Answer Option 2 Answer Option 3 Question 2 Answer Option 1 Answer Option 2 Answer Option 3 Submissions Submission 1 ('submission' template) Submission 2 question template title - the question being asked answer template title - the answer text correct - checkbox indicating this is the correct answer submission template (title -- make the field not required and hide it, rather than removing it as a global field... a trick I use) user - page field for parent /admin/access/users/; the person who took the test; maybe use the autocomplete inputfield here since loading 25,000 things into a select list will be problematic exam - page field for parent /exams/; the exam that was taken answers - multi-select page field (asm-select) that has all the answers they chose for the particular exam (any other relevant data you need to store) With some code you can check the correct answers of an exam vs. the submission answers and go from there.
    1 point
  11. 11ty is great. It's super flexible and doesn't lock you into a certain way of using it or fixed templating. Static site generators make a lot of sense for anything in the realms of corporate or product marketing page. You can keep all the stuff, which rarely changes in plain html. Form submissions and such can just be sent to a different headless endpoint. There's also things like Netlify CMS or similar tools, which allow you to trigger rebuilds whenever something in the underlying data changes. This is nice if you don't want to involve a dev every time some copy changes. For more dynamic content like e.g. comments I'd still not use a static site generator. They make sense if the cost of rebuilding for each change is outweight by the simplicity of deployment / putting the site up somewhere, but not really if you're constantly rebuilding. I don't really subscribe to the idea of having highly dynamic content just rendered client side, but people do use ssg's as an alternative to the dreaded SPA. Dynamic content is still all loaded/rendered in js, but at least it's multi page with some static bits.
    1 point
  12. Static site generators will probably be 'the next big thing'. You get way better performance and security by default than you could have with any CMS by definition. Which doesn't mean that you can't have dynamic content like comments, e-commerce etc. But instead of bundling that functionality, you're using external services, APIs, serverless functions and build automation to persist data and trigger builds whenever something changes. I'd recommend reading the Jamstack book (created by the Netlify people), you can get the ebook for free and it explains all the concepts related to SSG. By the way, you don't need NodeJS / NPM to have a static site generator. Though many SSG are built with JavaScript, they use NodeJS only as a runtime for the build step, not as an actual server. But there are SSG written in all kinds of languages - check out this site if you want to find one you like. One could argue, by the way, that the ProCache module for ProcessWire is also kind of a SSG generator, even though it works a bit differently than most tools on that list. My current recommendation is eleventy, I'm currently rebuilding a couple of my own sites as well as a ProcessWire-related project that I might release soon with it ?
    1 point
  13. The way I read the code in ProcessWire.php, this notice should only ever be triggered if you're bootstrapping PW and passing in a URL as the second parameter that contains a hostname not listed in $config->httpHosts. Is this generated by a bootstrap script? Which PW version do you use?
    1 point
  14. By default, the "Forgot Password" module is not turned on in v2.1. My thought was that lack of such a function is technically more secure (on any site or CMS). Why? Because having such a function active means your password is only as secure as your email (*though see note at end of this message). So I thought we'd start things out as secure as possible and let people adjust it according to their own need. But I'm rethinking that decision, and may change it to be 'on' by default. If you don't already have that "Forgot Password" module installed, it is relatively easy to reset your password with the API. Lets say that you lost the password for your account named 'admin' and you wanted to reset it. Paste this code into any one of your templates (like /site/templates/home.php in the default profile, for example): <?php $admin = $users->get('admin'); $admin->setOutputFormatting(false); $admin->pass = 'yo12345'; // put in your new password $admin->save(); …or if it's easier for you to copy/paste everything on one line, here's the same thing as above on one line: <?php $users->get("admin")->setOutputFormatting(false)->set('pass', 'yo12345')->save(); Replace "yo12345" with the new password you want and save the template. Then view a page using that template (like the homepage, in our example). The password for that account has now been reset, and now you are ready to login. Don't forgot to now remove that snippet of code from the template! Otherwise your password will get reset every time the page is viewed. Once logged in, here's how to install the Forgot Password capability: 1. Click to the "Modules" tab. 2. Scroll down to the "Process" modules. 3. Click "Install" for the "Forgot Password" module. That's all there is to it. You will now see a "Forgot Password" link on your login page. *ProcessWire's "Forgot Password" function is actually a little more secure than what you see in most other CMSs. Not only do you have to have the confidential link in the email, but the link expires in a matter of minutes, and PW will only accept password changes from the browser session that initiated the request. So an attacker would have to initiate the password change request and have access to your email at the same time, making it a lot harder for a man-in-the-middle snooping on your email.
    1 point
  15. I recently had to setup front-end system to handle logins, password resets and changing passwords, so here's about how it was done. This should be functional code, but consider it pseudocode as you may need to make minor adjustments here and there. Please let me know if anything that doesn't compile and I'll correct it here. The template approach used here is the one I most often use, which is that the templates may generate output, but not echo it. Instead, they stuff any generated output into a variable ($page->body in this case). Then the main.php template is included at the end, and it handles sending the output. This 'main' template approach is preferable to separate head/foot includes when dealing with login stuff, because we can start sessions and do redirects before any output is actually sent. For a simple example of a main template, see the end of this post. 1. In Admin > Setup > Fields, create a new text field called 'tmp_pass' and add it to the 'user' template. This will enable us to keep track of a temporary, randomly generated password for the user, when they request a password reset. 2a. Create a new template file called reset-pass.php that has the following: /site/templates/reset-pass.php $showForm = true; $email = $sanitizer->email($input->post->email); if($email) { $u = $users->get("email=$email"); if($u->id) { // generate a random, temporary password $pass = ''; $chars = 'abcdefghjkmnopqrstuvwxyz23456789'; // add more as you see fit $length = mt_rand(9,12); // password between 9 and 12 characters for($n = 0; $n < $length; $n++) $pass .= $chars[mt_rand(0, strlen($chars)-1)]; $u->of(false); $u->tmp_pass = $pass; // populate a temporary pass to their profile $u->save(); $u->of(true); $message = "Your temporary password on our web site is: $pass\n"; $message .= "Please change it after you login."; mail($u->email, "Password reset", $message, "From: noreply@{$config->httpHost}"); $page->body = "<p>An email has been dispatched to you with further instructions.</p>"; $showForm = false; } else { $page->body = "<p>Sorry, account doesn't exist or doesn't have an email.</p>"; } } if($showForm) $page->body .= " <h2>Reset your password</h2> <form action='./' method='post'> <label>E-Mail <input type='email' name='email'></label> <input type='submit'> </form> "; // include the main HTML/markup template that outputs at least $page->body in an HTML document include('./main.php'); 2b. Create a page called /reset-pass/ that uses the above template. 3a. Create a login.php template. This is identical to other examples you may have seen, but with one major difference: it supports our password reset capability, where the user may login with a temporary password, when present. When successfully logging in with tmp_pass, the real password is changed to tmp_pass. Upon any successful authentication tmp_pass is cleared out for security. /site/templates/login.php if($user->isLoggedin()) $session->redirect('/profile/'); if($input->post->username && $input->post->pass) { $username = $sanitizer->username($input->post->username); $pass = $input->post->pass; $u = $users->get($username); if($u->id && $u->tmp_pass && $u->tmp_pass === $pass) { // user logging in with tmp_pass, so change it to be their real pass $u->of(false); $u->pass = $u->tmp_pass; $u->save(); $u->of(true); } $u = $session->login($username, $pass); if($u) { // user is logged in, get rid of tmp_pass $u->of(false); $u->tmp_pass = ''; $u->save(); // now redirect to the profile edit page $session->redirect('/profile/'); } } // present the login form $headline = $input->post->username ? "Login failed" : "Please login"; $page->body = " <h2>$headline</h2> <form action='./' method='post'> <p> <label>Username <input type='text' name='username'></label> <label>Password <input type='password' name='pass'></label> </p> <input type='submit'> </form> <p><a href='/reset-pass/'>Forgot your password?</a></p> "; include("./main.php"); // main markup template 3b. Create a /login/ page that uses the above template. 4a. Build a profile editing template that at least lets them change their password (but take it further if you want): /site/templates/profile.php // if user isn't logged in, then we pretend this page doesn't exist if(!$user->isLoggedin()) throw new Wire404Exception(); // check if they submitted a password change $pass = $input->post->pass; if($pass) { if(strlen($pass) < 6) { $page->body .= "<p>New password must be 6+ characters</p>"; } else if($pass !== $input->post->pass_confirm) { $page->body .= "<p>Passwords do not match</p>"; } else { $user->of(false); $user->pass = $pass; $user->save(); $user->of(true); $page->body .= "<p>Your password has been changed.</p>"; } } // display a password change form $page->body .= " <h2>Change password</h2> <form action='./' method='post'> <p> <label>New Password <input type='password' name='pass'></label><br> <label>New Password (confirm) <input type='password' name='pass_confirm'></label> </p> <input type='submit'> </form> <p><a href='/logout/'>Logout</a></p> "; include("./main.php"); 4b. Create a page called /profile/ that uses the template above. 5. Just to be complete, make a logout.php template and create a page called /logout/ that uses it. /site/templates/logout.php if($user->isLoggedin()) $session->logout(); $session->redirect('/'); 6. The above templates include main.php at the end. This should just be an HTML document that outputs your site's markup, like a separate head.inc or foot.inc would do, except that it's all in one file and called after the output is generated, and we leave the job of sending the output to main.php. An example of the simplest possible main.php would be: /site/templates/main.php <html> <head> <title><?=$page->title?></title> </head> <body> <?=$page->body?> </body> </html>
    1 point
×
×
  • Create New...