Leaderboard
Popular Content
Showing content with the highest reputation on 08/30/2020 in all areas
-
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.5 points
-
This week I’ve been working on a combination of the core, and a PW audio-streaming project for a friend, working as a volunteer. There’s some overlap in these two things, as a lot of the core updates this week are somewhat related. It provided the needed motivation to finish a new feature I started working on in 3.0.149, which will be finally finished in 3.0.166. Actually, it’s finished now, but I haven’t bumped the version number on the dev branch yet—I’ll do that next week. The feature finished this week adds support for a new secure files option similar to the $config->pagefileSecure option (you may or may not have heard of), except that it allows you to configure secure files at the template level, rather than just site-wide level. In ProcessWire, when you unpublish or trash a page, or when you access control a page so that it’s not visible to guest users, the page’s files in /site/assets/files/page-ID/ can still be accessed directly by URL (like if someone bookmarked a PDF or something). So we have the $config->pagefileSecure option that lets you specify that the files should also be access controlled. This ensures (for example) that if a page is unpublished, its files can’t be loaded by anyone except a user that can view unpublished pages. But I rarely use this option because it is site-wide, and access controlled files take some overhead (being delivered by ProcessWire rather than Apache) and thus are something I’ve always wanted finer control over. That’s because most of the time, I don’t care too much about direct access to files because they are mostly images and endless variations of them. But there are also those specific cases where I really do need it. So being able to make it part of the template-level access control really makes it a lot more flexible, and hopefully makes the option a lot more obvious: There’s also that “Yes always” option (in the screenshot above), which is something we’ve not had before. This is useful when you want to have full control over a file download (or stream). Because the files would always pass through ProcessWire, you can hook any part of the process, keep a download counter, log downloads, etc. ProcessWire supports partial file delivery and delivery of specific byte ranges and seeking, which comes in especially handy with <audio> tags. In the case of the project I was working on this week, the audio streams are available only to logged in users (via LoginRegisterPro) and we had to find a way to make the audio files protected from direct access, so that they would only work as a stream through an <audio> tag to an authenticated user, not be accessible outside of that, and not be downloadable. That was all a bit of a challenge, but a fun one. If anybody ever runs into a similar need, be sure to send me a message. I have a few more things I want to work into version 3.0.166 before bumping the version number, so that’ll likely come by this time next week. I also hope to fully wrap up the new version of either FormBuilder or ProCache next week, followed by the remaining one the following week. Thanks for reading. Have a great weekend!2 points
-
Hi Dragan, and thanks a lot for your answer ? actually, that's the kind of method i've ended with, i just found strange that the result worked for one language, the default one, and not a secondary one but thanks to you all guys here, i've learnt a lot of ways to filter results with pw options (for example excluding unpublished pages after an include=all in a foreach) and everything works fine for me, it was a little surprising when i first ran into this 0 being french i mostly make multilingual website when it comes to job ones and gosh, pw made this so easy for even complicated application that the first time it just coughed a little i had to tell my surprise ? have a nice day2 points
-
Page View Statistic for ProcessWire is a module to log page views of the CMS. The records including some basic information like IP-address, browser, operating system, requested page and originate page. This module doesn't claim to be the best or most accurate. Advantages One of the biggest advantage is that this module doesn't require any external service like Google Analytics or similar. You don't have to modify your templates either. There is also no Javascript or image required. Disadvantages There is only one disadvantage. This module doesn't record visits if the browser loads the page from its browser cache. To prevent the browser from loading the page from its cache, add the following meta tags to the header of your page: <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="0" /> How to use The records can be accessed via the Setup-menu of the CMS backend. The first dropdown control changes the view mode. Detailed records View mode "Detailed records" shows all visits of the selected day individually with IP-address, browser, operating system, requested page and originate page. Click the update button to see new added records. Cached visitor records View modes other than "Detailed records" are cached visitor counts. Cached visitor records are collected on a daily basis from the detailed records. This approach is new in version 1.0.8 and ensures fast display even with many records. Another advantage is that the detailed records can be deleted while the cache remains. The cache can be updated manually or automatically in a specified time period. Multiple visits from the same IP address on the same day are counted as a single visitor. Upgrade from older versions Cached visitor counts is new in version 1.0.8. If you just upgraded from an older version you might expire a delay or even an error 500 if you display cached visitor counts. The reason for this is that the cache has to be created from the records. This can take longer if your database contains many records. Sometimes it might hit the maximally execution time. Don't be worry about that and keep reloading the page until the cache is completely created. Upgrades in general It seems that the upgrade function don't get called after an upgrade. This might be a bug in ProcessWire. This can lead to an error message ("Unknown column...") if you try to open the statistic page immediately after an upgrade. There are two ways to avoid this error message. Click the "Continue to module settings" button directly after the upgrade or open the Modules page and click the "Refresh" button. Time of view This module can record the time a visitor viewed the page. This feature is deactivated by default. To activate open the module configuration page and activate "Record view time". If activated you will find a new column (S.) in the records which means the time of view in seconds. With every page request, a Javascript code is added directly after the <body> tag. Every time the visitor switches to another tab or closes the tab, this script reports the number of seconds the tab was visible. The initial page request is recorded only as a hyphen (-). Execution time Starting with version 1.1.9 this module records the PHP execution time from the initialization of the module till the HTML output in seconds. Settings You can access the module settings by clicking the Configuration button at the bottom of the records page. The settings page is also available in the menu: Modules->Configure->ProcessPageViewStat. IP2Location This module uses the IP2Location database from: http://www.ip2location.com. This database is required to obtain the country from the IP address. IP2Location updates this database at the begin of every month. The settings of ProcessPageViewStat offers the ability to automatically download the database monthly. Please note, that automatically download will not work if your webspace doesn't allow allow_url_fopen. Dragscroll This module uses DragScroll, a JavaScript available from: http://github.com/asvd/dragscroll. Dragscroll adds the ability in view mode "Day" to drag the records horizontally with the mouse pointer. PhpUserAgent This module uses PhpUserAgent available from: https://github.com/donatj/PhpUserAgent. PhpUserAgent is required to filter out the browser type and platform from the server request. New in version 1.1.0 A new feature in version 1.1.0 offers the possibility to record user names of logged in visitors. Just activate "Record user names" and "Record loggedin user" in the module settings. New in version 1.1.3 A new feature in version 1.1.3 offers an internal WHOIS function. In the module settings you can switch between internal and external WHOIS. New in version 1.1.4 Detailed records can now be exported as CSV-file. The file contains all data of the specified date regardless of the pagination. The button is located at the bottom of the page. New in version 1.1.8 The UserAgentParser was exchanged with PhpUserAgent due to some complaints.1 point
-
Hello friends, Got a new project inquiry, the general requirements as below - Website Type - Online Examination Questions - Multiple choice Question with one selection for answer Expected Attendees - 25,000 students Registration required ( with OTP for confirmation ) with Online Payment mode After the exam, a student can see the score with results Admin - various type of lists ( highest mark scored students, categories of percentage etc ) Now, I have a doubt, what kind of Server hosting I should suggest to my client ? Is this 25,000 at a time very high load ? I dont know much about the server load / bandwidth . As like that, the requirement, is Processwire a suitable tool to achieve the goal ? if so, what are the modules, I should use, or a general structure / approach ? thank you1 point
-
Although doable, I'd only recommend a system like this to be implemented using ProcessWire if the rest of the project must (or already is) be highly coupled with PW and it doesn't make sense to decouple it for whatever reason. Otherwise I'd recommend you to implement it using another framework. PW shines in the content management part, and I love it, but it may present you with some limitations in its developer-experience department when you need to implement some business logic that relies heavily on integration of payment systems, user data input, generation of reports and especially tests. There's also a myriad of similar systems like this already implemented that you can use as a base, like this one https://github.com/LaravelDaily/Laravel-Test-Result-PDF (demo: https://www.youtube.com/watch?v=GmLFHGud7I8).1 point
-
Hi @swampmusic, i won't help but - if still needed - the help is on the way: https://weekly.pw/issue/329/1 point
-
thank you for your valid advice ... and yes this one is exactly what I thought, in order to save any complication1 point
-
@teppo The only reason I've started using wire()->pages->find() rather than wire('pages')->find() is just because the IDE (PhpStorm in my case) recognizes what wire()->files is, but doesn't recognize what wire('files') is... well it does, but it only knows that it could be any one of a bunch of API vars, rather than knowing it's the $pages API var. Substitute any API var for "pages" here, I'm just using pages as an example. Phpdoc data is able to traverse through variable and method names like wire()->pages or pages(), but not through strings like wire('pages'). It makes zero difference technically which you use, but at least as far as IDEs like PhpStorm goes, it's the difference between the IDE knowing what you are doing and not really knowing what you are doing. I like it knowing what I'm doing because it helps to find potential issues more easily, and it knows arguments, types and return values on methods I'm going to call, etc. You can also do the same by using phpdoc to tell it what type a particular variable is. For instance, the two bits of code below would have the same effect in terms of the IDE knowing what $pages is: // this: /** @var Pages $pages */ $pages = $this->wire('pages'); // or this: $pages = $this->wire()->pages;1 point
-
I'm going to start from the end, because this is the easy part (maybe) ? First of all, all the main concepts found from Wireframe have been around, in one form or another, for about a decade. Wireframe (the module) was developed a couple of years ago, and components were added about six months ago. Partials have been around since the very beginning, while components are a brand new thing. As for "should there be both partials and components", this is a good question going forward: Components are more versatile, but also more complex. There's a class (for business logic) and one or more views (for markup). Partials are a lot simpler. Even though you can pass params to partials and arrange them in directories by type or whatever, they are essentially just individual include files. In my typical project there's a need for both: by default I prefer the simplicity of partials, but if I really need to do something more complex on a per-element basis (justifying the mental and technical overhead of the class based approach), I go with a component instead. Heck, in one recent project I even surprised myself by using components within components. Not what I'd call an "easy to grasp setup" (which is always something that I try to emphasize in my work), but it gets the job done and was fun to work on. Will components and partials eventually merge into one? I don't know — maybe, maybe not. For the time being I quite like having them both around. In my projects they tend to solve different problems, even though they could be used interchangeably ? Oh, I'm sure that this makes a huge difference! As I've mentioned before I haven't really used custom Page classes, because — for my use cases — controllers provide same benefits, and then some more. Nevertheless, had custom page classes existed when I started working on Wireframe, there's a good chance that I would've taken an entirely different route ? I might've already touched some of the same topics earlier — particularly the one about having more than one type of object in Wireframe — but a few additional notes on this: You don't have to use anything you don't need. Layouts, views, and controllers are all optional. In many of my projects there's one layout, a bunch of views (often much fewer than there are viewable templates, since many templates are so simple that the layout file takes care of all the markup), and a few controllers (just for those templates that require something "more complex" behind the scenes; news or event containers / lists, page search, etc. The "everything is a component" approach is quite possible with Wireframe, though that was never really something I intended (or expected, to be honest!) The roots of Wireframe are in the MVC architecture, and that's where controllers, view, and model (which in this case is actually ProcessWire) come in. Components were intended as something that you can use to "fill in the gaps"; a bit like utility classes, for that matter. When you say that "Controllers are tied to the template of the viewed page", that's actually not entirely true: by default Wireframe will indeed only know that for a Page using the "basic-page" template it should check if a controller class called BasicPageController exists... but you can also make multiple pages share a single controller, change the controller on the fly, etc. Much of this is pretty new and I'm still experimenting with it, but I have high hopes that it will become even more useful in the future. As for the "couldn't that also be handled by the component" part: controllers do certain things that components don't, there's always exactly one controller active for a specific page at any given time, and these two work in a different context. The context of a controller is a page render request, while the context of a component is much smaller — essentially the subset of the request that it has been provided with. In my opinion it's best to keep them as separate concepts — at least for the time being — though once again who knows what will happen in the future ?♂️ I hope I don't really need to say this, but... feedback, comments, and (constructive) criticism is always welcome. I can't promise that I'll agree with all of it, but I'm happy to hear others' experiences nevertheless ?? -- I'm pretty sure that you've seen this already, but I've got a couple of site profiles online — https://github.com/wireframe-framework/site-wireframe-docs and https://github.com/wireframe-framework/site-wireframe-boilerplate. These are a bit dated by now (neither uses components, for an example), but they may provide some insight into the way I personally use Wireframe. I've been really looking forward to revisiting the weekly.pw website and sharing that as well, hopefully something I can get done before the end of the year. This site is also really, really dated (both visually and feature wise), and I'd also like to show off some of the new stuff in Wireframe ?1 point
-
1 point
-
I set up Laragon and successfully installed Processwire which is working great. I really like Laragon so far. Also installed WinSCP. I'm not doing enough work to warrant PHPStorm atm, but it looks good. Thanks for your suggestions.1 point
-
Hi @teppo Thx, it made click in my head ? I'll write about my journey and come up with suggestions at the end of the post: First try: <body> <?php if($page->is(['template' => [GemeindeGuru::tpl_teaser]])) { echo $partials->{GemeindeGuru::tpl_teaser}->body; } else { echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); } ?> </body> Nice, this already works ? 2nd try: <body> <?php switch($page->template->name) { case GemeindeGuru::tpl_teaser: echo $partials->{GemeindeGuru::tpl_teaser}->body; break; default: echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); break; } ?> </body> A lot better imho, but what if I wanted to add a custom 404 design? I'd need to duplicate the switch case: <body> <?php switch($page->template->name) { case GemeindeGuru::tpl_teaser: echo $partials->{GemeindeGuru::tpl_teaser}->body; break; case GemeindeGuru::tpl_404: echo $partials->{GemeindeGuru::tpl_404}->body; break; default: echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); break; } ?> </body> Not bad as well, but I wanted something better ? Next try: <body> <?php $body = $partials->{$page->template->name}->body; if($body) echo $body; else { echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); } ?> </body> ProcessWire\WireException Partial file not found: C:/laragon/www/kaumberg/site/templates/partials/home.php Fast-forward: I did some more testing and realized that I was actually mixing up partials and placeholders! The final (and great) version looks like this: // layouts/default.php <body> <?php if($placeholders->body) echo $placeholders->body; else { echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); } ?> </body> // views/home/body.php echo Wireframe::component("Header"); echo Wireframe::component("Search"); echo Wireframe::component("Slider"); echo Wireframe::component("News"); echo Wireframe::component("Contact"); echo Wireframe::component("Footer"); // views/teaser/body.php <div class='uk-height-viewport uk-flex uk-flex-center uk-flex-middle'> <div class='uk-text-center'> <div><img src='mylogo.svg' class='logo'></div> <div class='teaser'><?= $page->title ?></div> </div> </div> This simply means: If the view file /views/[template]/body.php exists, render it, otherwise render the default sections. That should make it really easy to create custom layouts for some templates whily still having all other assets and scripts in place (like seo, styles, analytics etc). I think I'm happy with this setup ? I realized, that I was missing the link between $placeholders->foo and /views/[template]/foo.php I read the docs again and I saw that this information IS already there ? My personal addition to this sentence: If you know where you have to look ? This illustration makes a lot of sense now. But I didn't get it quickly I have to admit. Here's my conclusion and suggestion: IMHO your docs are great explaining "how it works", but they could be improved in regards of "how I can work with it". In my experience this can make a huge difference in the experience of someone looking at it the first time. It's basically what you said earlier: Exactly that! I think if the docs where split into two sections, the first being "quickstart" and the second being what you already have, that would be brilliant. I know that what I'm asking for is a lot of work, but if we (you ?) want to build some kind of standard, it should be as easy as possible for everybody to adopt to it ? Finally I've got a question/request that you have to review from a technical point of view. As stated above, it took some time for me to grasp this easy concept: $placeholder->default loads /views/MyTemplate/default.php $placeholder->head loads /views/MyTemplate/head.php If you look at the rest of wireframe this becomes quite obvious: echo Wireframe::component("Header") --> /components/Header/default.php echo $partials->menu->top() --> /partials/menu/top.php echo $placeholders->body --> /views/[template]/body.php This really confused me when starting with wireframe! I'd expect placeholders to live in /placeholders or, the other way round, the variable to be called $views echo Wireframe::component("Header") --> /components/Header/default.php echo $partials->menu->top() --> /partials/menu/top.php echo $views->body --> /views/[template]/body.php These 3 lines would make it obvious what options you have in your layout file. But maybe that's because I've already a better understanding of what's going on, so maybe other newcomers could comment on this? What I was really missing was a guide that helps me go through a typical web project: You got a design, you got a PW installation, you installed Wireframe, what then? Now after 2 days of testing this was the way that worked great for me (so far): Set wireframe as alternate template file for all templates that should be handles via wieframe (for this walkthrough we take home.php and basic-page.php of the default site profile). Create a default layout: /layouts/default.php <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?= $page->title ?></title> </head> <body> <h1>I am the default layout</h1> </body> </html> Open the root page of your site and the 404 page --> both should display the default layout We want a common page header and footer that is displayed on all templates. Shared markup with little to none business logic is best placed in a partial. Create 2 files: /partials/header.php <div style='border: 1px solid blue; padding: 20px;'>I am the page header</div> /partials/footer.php <div style='border: 1px solid red; padding: 20px;'>I am the page footer</div> Now you can output these partials in your layout's body: <body> <?= $partials->header ?> <h1>I am the default layout</h1> <?= $partials->footer ?> </body> What if we wanted a slider on the frontpage? And ONLY on the frontpage? That's what view files are for and they are accessed via $placeholders. (Don't you think that this is really misleading?) /views/home/slider.php <div style='padding: 20px; border: green;'>I am a SLIDER</div> And add it to the layout: <body> <?= $partials->header ?> <?= $placeholders->slider ?> <h1>I am the default layout</h1> <?= $partials->footer ?> </body> Now open both pages on the frontend and you'll see that the slider does only show up on pages having the [home] template. That magic is simply applied via file structure /views/[template]/slider.php Ok now we want to add the current local time of the user visiting the page. That will obviously need some PHP logic and as we want to separate concerns as much as possible we do NOT want to put that code into a partial. Instead, we create a component that consists of business locig (a PHP class extending \Wireframe\Component and a view file that is responsible for the output. <?php namespace Wireframe\Component; class LocalTime extends \Wireframe\Component { public function render() { return "<div> Your timezone: <strong>XXX</strong><br> Your local time: <strong>XXX</strong> </div>"; } } And add it after the footer of our layout: <body> <?= $partials->header ?> <?= $placeholders->slider ?> <h1>I am the default layout</h1> <?= $partials->footer ?> <?= Wireframe::component('LocalTime') ?> </body> Nice! We get the output at the bottom of all of our pages! We know that doing lots of output within php classes is ugly, so we refactor our setup a little and adopt another concept of wireframe: View files for components: /components/LocalTime.php <?php namespace Wireframe\Component; class LocalTime extends \Wireframe\Component { } /components/LocalTime/default.php <div> Your timezone: <strong>XXX</strong><br> Your local time: <strong>XXX</strong> </div> Of course, this is no a lot better for now, but it is a lot more future proof! Check your frontend, it should still display the same markup for your local time. Now we can add the "business logic" to our component: <?php namespace Wireframe\Component; use ProcessWire\WireData; use ProcessWire\WireHttp; class LocalTime extends \Wireframe\Component { public function __construct() { $http = new WireHttp(); $data = new WireData(); $data->setArray($http->getJSON("http://worldtimeapi.org/api/ip")); $this->timezone = $data->timezone; $this->unix = $data->unixtime; } } And update our view to output the component properties (note how they magically become available as view variables!): <div> Your timezone: <strong><?= $timezone ?></strong><br> Your local time: <strong><?= $unix ?></strong> </div> Go to your frontend and see the magic! Your timezone: Europe/Vienna Your local time: 1598007959 What, you don't want a unix timestamp? Ok... let's provide a formatter method so that you can easily output the time in different formats: <?php namespace Wireframe\Component; use ProcessWire\WireData; use ProcessWire\WireHttp; class LocalTime extends \Wireframe\Component { public function __construct() { $http = new WireHttp(); $data = new WireData(); $data->setArray($http->getJSON("http://worldtimeapi.org/api/ip")); $this->timezone = $data->timezone; $this->unix = $data->unixtime; } public function date($format = "d.m.Y H:i:s") { return date($format, $this->unix); } } @teppo now I got stuck again ? How would I display that formatted date in my view file for this component? Or would I need to provide the date format as variable for the constructor ( echo Wireframe::component('LocalTime', ['format' => 'Y-m-d']); ) PS: Regarding the last question. Controllers do expose methods to views. Is there a similar concept for components? I did implement the local time as component because this does not belong to a single template (then it would have been a controller, right?)1 point
-
@rookie I wish you hadn't said that. I've been using Windows since 1987. Probably need a blood transfusion to get it out of my system.1 point
-
Althought Laragon is a great choice. You can also use Docker if you want ? Here I made a simple config for PW https://github.com/joyofpw/docker ?1 point
-
1 point
-
I tried them all => go for Laragon => fast => easy config easy ui https://laragon.org/why-laragon/1 point
-
@NooseLadder ... or you take the easy way and get you a full copy of Laragon! It has everything you may need or not. Including One-Click-SSL-Certificates in your local dev environment to use and test full https protocol. And much more. ? see what others mean: site:processwire.com/talk laragon1 point