Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 09/16/2019 in all areas

  1. I just wanted to mention. I found a new job as a front-end dev at https://backslash.ch. Since 2 months there already and enjoy it. We work with in-house CMS specially targeted to governments. So a lot less PW for me in the future, but I'll use it as my tool for private projects.
    5 points
  2. @teppo @wbmnfktr Wow this discussion derailed a bit, but here we are ^^ Teppo, thanks for the pointers regarding target blank, I didn't really have that on my radar. Makes perfect sense though, I'll definitely reconsider my current approach! Even more off-topic, but I feel a bit smug now about having isolated that logic into a twig template in my current project, so now I can change it in a central place: {# blocks/link-start.twig #} {#- # Renders a start tag for a link. The link will automatically have target="_blank" # and rel="noopener" if the link leads to an external domain. # # @var string url The target (href) for the URL. # @var bool is_download Should the link be marked as a download (for direct file downloads)? # @var array classes Optional classes for the anchor. -#} <a href="{{ url }}" {%- if is_download is defined and is_download %} download{% endif -%} {%- if classes is defined and classes is not empty %} class="{{ classes|join(' ')|trim }}"{% endif -%} {%- if isExternalUrl(url) %} target="_blank" rel="noopener"{% endif %}> I'll just get rid of the last part ? Thanks to everyone participating in the discussion! I think it's great how different perspectives come together that provide everyone new insights & best practices ...
    4 points
  3. Maybe take a look at URL segments? https://processwire.com/docs/front-end/how-to-use-url-segments/
    3 points
  4. Never tried it with the core module but Croppable Image 3 works really good and has some nice additional settings and features. Maybe worth a try.
    3 points
  5. https://vitalydidenko.com https://skyscrapers.nurgulyashyrov.com/ We use Nginx for all PW websites, but these are the ones I have on my own servers. Please DM me if you find any security issues.
    3 points
  6. Modules Manager 2 provides an easy to use interface to download, update, install, uninstall and configure modules. It is meant to provide an optimized alternative to the core ProcessModule dashboard. Maybe @ryan agrees to merge it to the core at some point when it is finished and polished. Features: Seamlessly download, update, install, uninstall or delete modules Live-Search (aka find as you type) for module names Live-Search (aka find as you type) for categories Browse new and unkown modules from the modules directory on modules.processwire.com Choose your favorite layout (cards, reduced cards, table) Modern UIKit design (therefore only works with AdminThemeUikit) Caches the module list from modules.processwire.com directory locally. What is Modules Manager 2? Why a new module manager? Some people including myself think that the actual module installation in ProcessWire could be improved in some places. Make it easy for ProcessWire beginners and power users Offer better discoverbility to find the right module Make it easier and faster for powerusers to manage modules A manager that list all official modules is a feature, that many other frameworks/CMS's like ModX, WordPress or PrestaShop have by default. What are the disadvantages of the actual core module interface? Installation of a module is not very user-friendly: You have to be aware where to get new modules, search for a module, copy or remember the module name or URL, go back to your ProcessWire installation, paste the module name(URL, click on "get module info" and finally install the module It only displays installed modules, not the ones that are available in the modules directory Uninstalling a module requires you to go to the module detail page, click a checkbox and then submit the change. After that you have to go back to the module overview page. It only displays installed modules, not the ones that are available in the modules directory, so it makes discovering modules hard BETA software Use this module at your own risk. I am not responsible for any damage or unexpected behaviour. Some things might not work fully, please see the TODO list for details. I need your feedback and help This module is still in development and I am happy to discuss with you and get some feedback. What do you like? What is missing? What could make the process even easier? Ask, suggest or provide pull requests. You can download the module at https://modules.processwire.com/modules/modules-manager2/ or from Github: https://github.com/jmartsch/processwire-modules-manager2
    2 points
  7. In the last few weeks... or almost months... I worked on a project for a restaurant. Sad to say that the company behind the restaurant went out of business before the project could be finished - or was paid or the website ever saw the light of day. As there is kind of a lot of work in it... but yet a lot of customization... I decided to create a site profile of it and make it public as ProcessWire Barebone Site Profile. Right now I'm stripping out every client detail and finish some functions and details that were planned a few weeks back so you can build right on top of it, if you like or want. Maybe it's only a playground for someone or an inspiration for how to f**k up data-mangement... but hey... better than a ZIP file on a backup drive. ? As the project was and is super tailor-made to that client, it may not work for a lot of restaurants out there, but nonetheless I don't want miss the opportunity to offer a foundation for upcoming restaurant projects you may face. The project had a huge focus on dishes, beer and wine variations so those templates are kind of feature-rich. You might want to rip out some of it as most restaurants don't focus that much on those details. Important details I want to be honest and clear about: this profile does NOT include a highly polished and usable frontend as the design is paid and therefore can't be made public the frontend will be a collection of re-usable snippets to show every possible detail of each page, item and whatever the whole site profile will be without any support - future updates aren't planned right now the site profile will be released in single-language setup only (english) existing translations (as seen in parts of the screenshots) will be changed to english existing data, for example dishes, will be replaced with demo content if you, one of your clients or anyone else wants to use this profile - feel free to do so Pro Modules were already stripped out of it only public modules were used and are included with the profile itself the full module list will soon be published on Github the full README will soon be published on Github the full LICENSE will soon be published on Github So... yes... that's just a short preview of an upcoming site profile. As mentioned before, the site profile will most likely never ever receive any future updates, so it will be available on Github only. It's not planned to publish this as a full featured site profile, yet. More details, screenshots and the site profile itself (of course) will be available soon. Questions? Please feel free to ask. Github Repository: https://github.com/webmanufaktur/pwbrestaurant
    2 points
  8. Thanks alot @Juergen! If you wan't to name the button automatically based on the page-table label or the label/title of the allowed templates, use this hook: $this->addHookBefore('InputfieldPageTable::render', function ($event) { $table = $event->object; // Make sure only for fields with a single template… if (count($table->hasField()->template_id) > 1) { return; } // Use the field label… $label = $table->hasField()->label; // or… use the template label or title… $label = $this->templates->get(['id=' => $table->hasField()->template_id])->get('label|title'); // don't miss the use($label part)… $this->addHookBefore('InputfieldButton::render', null, function (HookEvent $event) use ($label) { $button = $event->object; if ('button' == $button->name) { // add label and translatable Add string… $button->attr('value', __('Add') . ' ' . $label); } }); });
    2 points
  9. It's like someone is putting wire between some processes. Oh... wait...
    2 points
  10. https://www.litespeedtech.com/products/cache-plugins Where is the ProcessWire LSCache module ??
    2 points
  11. Nginx' performance advantages over Apache were built on three factors: modern-day multiprocessing in the server, a lot less overhead due to reduced functionality and memory caching. Over the last five years, Apache has greatly reduced that gap by adapting Nginx' multiprocessing approach (one keyword there is the event MPM module), so Apache isn't spending most of its time spinning up and tearing down whole server instances anymore. File system access has greatly improved with solid state disks, too. Apache still has a lot more functionality, and its distributed config file approach, most prominently the ability to make configuration changes with a .htaccess file inside the web directories, hurts performance. Its dynamic module loading approach and the dozens of pre-installed modules most distributions ship also take up processing time and memory. Nowadays, Apache can be stripped down a lot and compiled to be head to head with Nginx, though few actually care to do that, since it also means removing functionality one might need in the future. A stock Apache is usually still quite a bit slower and reaches its limits faster (about the factor 2). This becomes an issue under heavy load or on slow machines. Where Nginx still shines brightly is load balancing. Apache can do it too, but with Nginx it is straight forward and well documented, having been there for a long time. For those interested in a bit of (highly subjective) history: for a long time (speak eighties and nineties), the classic forking mechanism that was common on *nix OSes was the way to do multiprocessing in network servers, and therefore in Apache too. This meant spawning a full copy of the server process and initializing it, then tearing it down when the request was done. Apache brought a small revolution to that approach by implementing preforking, meaning to keep spare server instances around to fulfill requests with little delay. After a while, there were other approaches too when faster multiprocessing approaches become part of common operating systems, like multi threading, which is supported by Apache's "worker" multiprocessing module (MPM). There were, however, big caveats with using other MPMs. Since file systems used to be slow, sometimes awfully so, in the old days, and since the classic CGI approach of starting an executable from the file system, supplying it with information through environment variables and standard input and capturing its standard output was a security nightmare - even without thinking about shared hosting - nifty programmers included full languages interpreters inside Apache modules. mod_perl and mod_php became the big thing, the latter coming to dominate the web after a few years. These interpreters, though, often had memory leaks and issues with thread isolation, meaning at best that an error in one thread tore down numerous other sessions and at worst that the server had a propensity for information leaks, remote code execution and privilege escalation attacks, the former security nightmare squared. Thus, these tightly integrated interpreters more or less locked their users into the classic prefork approach where every instance is its own, basically independent process. With PHP as the market leader not evolving in that regard, things were frozen for quite some time. This was when Nginx conquered the market, first by serving static HTML and associated resources with lightning speed (CMSes generating static HTML were still a big thing for a while), but soon by taking care of all the static stuff while handling the dynamic things off to Apache and caching parts of its responses in memory. Finally, though, PHP finally got a fresh boost and grew stable enough for its engine to re-use interpreter instances. It was easier to contain things inside an interpreter-only process instead of dealing with all the server peculiarities, so FastCGI daemons finally became stable, known and used, and suddenly the need to have the language interpreter contained in the web server fell away. Apache got leaner and Nginx more flexible. Caching servers like Varnish became popular since it suddenly was relatively easy to build a fast, nice, layered caching solution with a combination of Nginx, Varnish and a full fledged web server like Apache or IIS, able to serve thousands of highly dynamic and media rich pages per minute. About that time, SSL grew in importance too, and hosting providers learned to love Nginx as a means to route domains to changing backends and provide fast and easily configurable SSL endpoint termination. Over the last years, Nginx got other features like generic TCP protocol load balancing that offset it from other servers and make it more into a one-stop solution for modern web applications. It does boost its popularity that Nginx is often the first (or the first major) web server to ship evolving technologies, making the front pages and pulling in early adopters, http/2 being one of the most prominent examples there.
    2 points
  12. @pwired I'm not sure if you really know what or about what you are talking? But I definetly know that I really dislike such sort of posts. Please revert back to kindly posts only. And, if you want to answer me to this post, please do it via PM to me. (I'm into weekend now and only can answer on monday next week, but will definetly do.)
    2 points
  13. I may have missed some of the points made here (sorry in advance), but regarding links opening in new tab/window: please keep in mind that this is a problem from accessibility point of view. For typical desktop users with decent eye sight etc. this is usually not a problem – and in fact it may be nice in some cases that a link automatically opens in a new tab – but for most screen readers (apps, that is – and thus their users as well) it's a real problem. From what I've heard/read, it can entirely mess up their understanding of the context. WCAG advises that if you're about to open a link in a new window, you should provide a clear signal that this is going to happen. This can, for an example, be a note at the end of the link text: "(opens in a new window)". If you care about accessibility, think twice before you use target="_blank". And for those cases where the client requests this, be sure to inform them that it will likely cause issues for some users. If the client still wants to go with it then by all means do it, but I've found this argument to resonate quite well with most sensible people ?
    2 points
  14. I've encountered users who don't even know what a tab is, and are confused when they cannot get back to the site they were on before by just hitting the back button! It's no use keeping your site up in the background if your users don't know how to get back to it ? So on principle I agree with @adrian on this, but our clients still keep asking for target="_blank", so that's that.
    2 points
  15. Hi @jens.martsch, This looks awesome! Even without the todo list, this is a huge improvement to the current way of installing and searching for modules. I really like the listing of modules. Some quick questions: Why call it Modules Manager 2? Why not just ModulesManager? I might have missed it; Is it possible to read/find modules that have been moved physically to the modules folder on one's site but have not yet been installed? This will cater for modules that are not online in ProcessWire's module's directory Great work! PS: Nice overview video, btw!
    1 point
  16. You can check whether page rendered in admin if so, do nothing. if (strpos($_SERVER['REQUEST_URI'], $this->wire('config')->urls->admin) === 0 || $this->wire('page')->template->name == 'admin') return;
    1 point
  17. yeah sure, it's Open Source ?
    1 point
  18. As long as there is no WordPress involved. ?
    1 point
  19. Variations 002 (released 16/09/2019) Happy to announce the latest release of Variations. Changelog ProcessWire 3.x support only. New GUI to match AdminThemeUiKit Save and exit or save and add more variations configuration or attributes (in modal) More granular control of custom column types and definitions: DECIMAL, FLOAT, DOUBLE, TINYINT, INT, VARCHAR, TEXT, DATETIME, TIMESTAMP, BOOLEAN, etc Decimal type better suited for price data compared to Float/Double Boolean type, useful for Yes/No type data, e.g. for sell out of stock? yes/no Datetime/Timestamp fields if required. Column definitions: null, precision, scale, length, default value, etc. Required fields: if column is not nullable and no default provided, it becomes a required field. E.g. can set price not to be null. Default fields: For uses such as prefixing SKU- Apologies that this has taken a while to update. Upgrading If upgrading from version 001, ProcessWire's Filecompiler will fight you. You might get errors such as Class ProcessVariations not found in file..... If you get such errors, first, try to clear your cache and compiled files several times. As a last resort, if clearing cache doesn't work (and I don't like this workaround), temporarily change the code in the offending file, e.g. in VariationsRender.php, where it says class VariationsRender extends ProcessVariations, change that to class VariationsRender extends WireData. Refresh the modules page until the errors disappear, then revert back to the original text (class VariationsRender extends ProcessVariations). I hate to ask you to do this but I've had it with the Filecompiler. Documentation Documentation can be found here. However, it still needs updating to reflect latest changes outlined above. For now, please note that we've added an extra setting for setting the default date and time formats for datetime/timestamp custom columns/subfields. This is available in the Input Tab of the field. Screenshots Thanks!
    1 point
  20. For a while I tested litespeed, worked fast and simple, I did not have to change anything on the server except turn it on through a plugin in the controlpanel (directadmin) only thing was the cost per month, but the speed was great. Here is a link to a page that talks about apache, nginx and litespeed: https://www.litespeedtech.com/products/litespeed-web-server/compare-litespeed-apache-nginx Just found openlitespeed as well, the open source version of litespeed: https://openlitespeed.org/
    1 point
  21. So true. It's a nice idea, but CSS content is indeed problematic. Sometimes you want to add otherwise meaningless (visual) content (like icons) with CSS content and it turns out that some screen readers read them out loud, which can be quite confusing. There's no aria-hidden for CSS content, so reliably hiding said content from screen readers can be a real hassle. On the other hand there are times when you actually want to provide content for screen readers with this technique – yet it turns out that some of them will happily disregard it. In my experience it's almost never a good idea to add content with CSS ?
    1 point
  22. No blog post this week because I don’t have anything new or interesting to write about just yet (unless you like to hear about repairing fridges and clothes dryers). But I’ve been focused primarily on completing the FormBuilder updates that I mentioned in last week’s blog post, and it’s looking very good, though I’ve still got a little further to go with it. When it comes to multi-page forms, I’m trying to cover all of the exceptional cases where sessions expire, cookies clear, etc., and want to make sure a form-in-progress continues to work through all these situations. Multi-page forms can be potentially long and people can invest lots of time in them (relative to regular forms), so trying to make it all as resilient as possible. This takes lots of time developing and testing, so that’s what I’ve been doing. I’ll be doing some of that next week too, though also have been planning to dig back into the core issues repo and start working through some of those in preparation for a new master version this Fall. Hope you all have a great weekend!
    1 point
  23. @teppo's argument here is solid and I thought... cool... just add some details to those links with CSS-magic: a[target=_blank]:after { content: " (opens in a new window)"; } BUT... not all screenreaders support that feature... all I found was kind of a mixed result. https://www.powermapper.com/tests/screen-readers/content/css-generated-content/ Just in case for those who had a similar idea. ?
    1 point
  24. You can do that in an onchange handler for your number input. The limit is stored in the data-multiplier-limit of a hidden input with an ID of "Inputfield_" plus the name of the fieldset and "__multiplier_rows". If, for example, your "No Of Users" field is named "usercount", adding this snipped at the bottom of your template should do the trick: <script> $('#Inputfield_usercount').on('change', function(evt) { var newlimit = $(this).val(); $('Inputfield_myinputs__multiplier_rows').data('multiplier-limit', newlimit); }); </script> This is untested, though. I'm heading out in a minute, but I'll take a closer look tomorrow. Did you add the "myinputs__multiplier_rows" label yourself? If not, then I missed to include the code to properly hide InputfieldHiddens in the module and need to fix that.
    1 point
  25. This doesn't warrant a full tutorial, but I wrote a little function that will recursively search through a repeater field to find the first non-empty field matching a list of fields to look for. I needed something like this to generate a fallback for SEO fields (og:image, og:description et c.). Here it is: <?php namespace ProcessWire; /** * Find the first non-empty field out of a list of fields in a repeater or repeater matrix field. * Will recursively search through nested repeaters until it finds a non-empty * field. * * @param RepeaterPageArray $repeater The field to search through. * @param array $fields A list of fields the function should look for. * @param array|null $allowed_repeater_types All the Repeater Matrix types the function will check. Leave empty to allow all. * @param array|null $allowed_repeater_fields All the repeater fields the function will check recursively. Leave empty to allow all. * @return void */ function firstRecursiveRepeaterMatch( RepeaterPageArray $repeater, array $fields, ?array $allowed_repeater_types = null, ?array $allowed_repeater_fields = null ) { // iterate over the items of the repeater foreach ($repeater as $current) { // if the function is currently inside a repeater matrix field, // skip this item if it isn't one of the allowed types, unless // allowed_repeater_types is empty (all types allowed) if ( $current instanceof RepeaterMatrixPage && is_array($allowed_repeater_types) && !in_array($current->type, $allowed_repeater_types) ) { continue; } // get all fields of the current item foreach ($current->getFields() as $field) { $name = $field->name; // if the current field is another repeater, check it recursively $fieldtype_class = $field->getFieldType()->className(); if ($fieldtype_class === 'FieldtypeRepeater' || $fieldtype_class === 'FieldtypeRepeaterMatrix') { // continue with the next item if the field name isn't one of the // allowed repeater fields, unless allowed_repeater_fields empty (null) if ( is_array($allowed_repeater_fields) && !in_array($name, $allowed_repeater_fields) ) { continue; } $deep_search = firstRecursiveRepeaterMatch( $current->get($name), $fields, $allowed_repeater_types, $allowed_repeater_fields ); // if the deep search inside the repeater // finds something, the function ends here if ($deep_search !== null) { return $deep_search; } } // if the current field name is one of the requested // fields, return it's value if it isn't empty if (in_array($name, $fields)) { $value = $current->get($name); if ( // check for empty values !empty($value) // if the value is any wirearray, check // if it has at least one item && (!$value instanceof WireArray || $value->count() > 0) ) { return $value; } } } } // if the function reaches this point, there is no match in the entire tree return null; } Can be used like this: $seo_description = firstRecursiveRepeaterMatch( // content sections fields, a repeater matrix with multiple types $page->sections, // look for the first non-empty instance of any of those fields ['body', 'html_basic', 'html_full'], // only check sections of the following types ['section_text', 'section_columns', 'section_accordion'], // only check the following nested repeaters recursively ['columns', 'accordion'] ); Wanted to share because I thought it could be useful to others. It should be easy to adjust the matching condition from non-emtpy fields to some other condition, depending on the use case ...
    1 point
  26. Hmm, I'm not convinced that just because something used to be artificially defined as standard that means it is necessarily "ideal". I've seen so many people not knowing that the browser has a back button/feature (let alone the history...), getting confused by navigating away from the site, using Google just to find it again. But these individuals in question are aware of the tabs feature of the browser – and they do use it – as tabs are promptly visible (at least on desktop) and such users only use what they see right in front of them. Menus and shortcuts? Alien to them. The article is certainly subjective yet tries to look objective. The article's points certainly have merits, but just as our main topic, I think requirements vary and so do implementations based on that...
    1 point
  27. I know I am getting OT, but I almost never use target="_blank" anymore. Here are a few thoughts: https://css-tricks.com/use-target_blank/ I think the key thing for me is that it's really hard to prevent opening a new tab when it is used, but it's really easy for the user to choose to open in a new tab if they want, so my rule is that unless the user will lose unsaved form data as a result of opening a link in the same tab, I never force a new tab.
    1 point
  28. A few months back I gave Linux another chance. I was looking for something that could improve my workflow in some kind or the other and yes... I'm still on Linux. Tried a lot of distros and windows managers since then - right now I'm super happy with Manjaro and the i3 (i3-gaps) window manager. Works pretty pretty well so far.
    1 point
  29. Well... why don't you rename your module to ZekaProcessDashboard?
    1 point
  30. Not another Markup Fieldtype ? https://github.com/BernhardBaumrock/FieldtypeRockMarkup What do you think of a Fieldtype that does nothing else then rendering runtime markup from files that you provide in the settings or at runtime? In addition to that, I'd like the field to trigger javascript events to be usable for other fieldtype modules (like a charts module or a grid module). Javascript events would be necessary on field load (ajax/non-ajax), on collapse of the field, on click of a tab. When working with charts and RockGrid this has sometimes become a pain and I've implemented (sometimes dirty) fixes for that in different modules in different ways. I think it would make sense to have a solid base for such kind of modules (fieldtypes) that use some kind of standard events for standard situations. I'm especially interested in your opinions @Beluga because of the upcoming RockTabulator module and @kongondo because of your existing module. I think there are already similar modules by @Robin S and @kixe ? Thx for your input ?
    1 point
  31. Try to refresh modules cache. Also, you could try to remove everything in the assets/cache folder.
    1 point
  32. Greetings Manaus, As luck would have it, I just completed a project where the client needed to have a form on the front end where he can upload a CSV file and have it create pages. Using ProcessWire, I put together a system that does the following: 1. Presents the visitor with a simple form with a "title" and an "upload" field 2. Creates a parent page with the title = "title" 3. Uploads and saves the CSV file to the parent page 4. Opens the CSV file using PHP's native fopen 5. Reads the rows of the opened CSV file using PHP's native fgetcsv function; a simple counter skips the first row (which is assumed to be a header row) 6. Creates children of the "title" page, each with the name of the data in first column of each row 7. Presents a "success" message when it works. Your needs might be different, so just vary the code as needed, or post a follow-up question for more details. Here's the code (with my documentation added): <div class="chart_form_box"> <?php if ($input->post->title) // Confirm that a proper submission happened: if the form submission at least has a title, use the field entries to create a new parent page. { // Set a temporary upload location where the submitted "csv_import" file is stored during form processing (will be removed at the end of the process). $upload_path = $config->paths->assets . "files/csv_uploads/"; // This folder must exist in the directory structure. // New wire upload routine for the CSV file. $csv_upload = new WireUpload('csv_upload'); // Reference field name in HTML form that uploads the CSV file. $csv_upload->setMaxFiles(1); // Allow only 1 CSV file upload. $csv_upload->setOverwrite(false); // Rename any current files, instead of overwriting them. $csv_upload->setDestinationPath($upload_path); // Use the temporary location set above to place the CSV upload. $csv_upload->setValidExtensions(array('csv')); // Only allow CSV files. $csv_uploads = $csv_upload->execute(); // Execute CSV file upload. // First round of page creation (for the parent page, created from a title and a CSV file upload). // Set up submissions in the ProcessWire page tree. $np = new Page(); // create new page object. $np->template = $templates->get("windsor_parent"); // Set template for pages created from form submissions. $np->parent = $pages->get("/windsor-list/"); // Set parent for pages created from form submissions. $np->of(false); // Sanitize form submissions and store the submissions as values in the template fields for the new parent page. $np->title = $sanitizer->text($input->post->title); $np->name = $sanitizer->pageName($input->post->title, true); // Create a URL-friendly "name" based on the title. // Save/create the new parent page. $np->save(); // Run the file upload for "csv_upload." foreach($csv_uploads as $csv_upload) // Loop through the CSV uploads, even if there is just one. { $pathname = $upload_path . $csv_upload; // Store the temporary CSV file in the path dedicated to that purpose. $np->csv_upload->add($pathname); // Store the uploaded CSV file in the "csv_upload" field of the template. $np->message("Added file: $csv_upload"); // Include a message regarding the file that was uploaded. unlink($pathname); // Remove the file from the temporary folder since it is no longer needed after it is uploaded. } $np->save(); // Save page again after CSV file upload // Beginning of routine to save children of the page saved above: derived from the rows in the CSV file. if (($objCSV = fopen("http://www.pwplayground.com/site/assets/files/$np->id/$np->csv_upload", "r")) !== FALSE) // Open the uploaded CSV file that was saved above. { ?> <!-- Create a list of the rows in the CSV file --> <?php // Loop through the rows in the CSV file using PHP's fgetcsv function, with the goal of placing the data in each row of the CSV file into fields in our "windsor_child" template. $i = 0; // Set up a counter variable. This is used to keep track of iterations that will correspond to rows parsed in the CSV file. We will use this later to skip the first row, which is a header row. while (($objArr = fgetcsv($objCSV)) !== FALSE) // Instruct fgetcsv to add to the $objarr as long as there is a row to read. { if( $i >= 1 ) // Only run the process beginning with the 2nd row, thereby skipping the header row. { // Beginning of routine to save child pages based on the rows in the uploaded CSV file. $np2 = new Page(); // create new page object $np2->template = $templates->get("windsor_child"); // Set template for child pages. $np2->parent = $pages->get("/windsor-list/$np->name/"); // Set parent for child pages to be the page created above. $np2->of(false); // Set output formatting off during the page-creation process. // Sanitize the rows of the CSV files, just in case someone uploads something evil, then store the values in each row into the template fields for the new page. $np2->title = $sanitizer->text($objArr[1]); $np2->name = $sanitizer->pageName($np2->title, true); // Create a URL-friendly "name" based on the title. $np2->windsor_count = $sanitizer->text($objArr[0]); // Store the first column of each row in the "windsor_count" field of the template. $np2->windsor_name = $sanitizer->text($objArr[1]); // Store the second column of each row in the "windsor_name" field of the template. // Save/create the new child page. $np2->save(); // End of routine to save child pages based on the rows in the uploaded CSV file. } $i++; // Move the counter variable up one tick with each iteration. } ?> <?php } fclose($objCSV); // End of routine to save children of the page saved above. These are derived from the rows in the CSV file. ?> <!-- If uploading the CSV file, and creating the new parent chart page and children pages were all successful, then: (1) Confirming success; (2) Present option to jump to the newly created page; (3) Present option to create another new page. --> <div class="admin_success_box"> <p>SUCCESSFULLY CREATED A NEW CHART PAGE CALLED "<?php echo $np->title ?>"</p> <p><a href="<?php echo $np->url?>">VIEW THE NEW CHART PAGE </a>-- or --<a href="<?php echo $page->url ?>"> CREATE ANOTHER CHART PAGE</a></p> </div> <?php } // / if ($input->post->title) ?> <!-- Actual entry form that allows the user to set a title and upload a CSV file --> <h1>Use the Form Below to Create a New Parent and Children from a CSV</h1> <form action="<?php echo $page->url ?>" method="post" enctype="multipart/form-data"> <p><label class="create_chart_field_label" for="title">Title</label> <input type="text" class="create_chart_long1" name="title" value="<?php echo $input->post->title ?>"></p> <p>Upload 1 CSV File: <input type="file" class="create_chart_field1" name="csv_upload" /></p> <button class="admin_submit" type="submit" name="submit">CREATE CHART!</button> </form> </div> <!-- / chart_form_box --> My apologies for the formatting. (Also, the "chart" references are specific to my particular project.) As you can see, much of this involves using ProcessWire's API to create pages, but with a few extra steps for the CSV material. Thanks, Matthew
    1 point
×
×
  • Create New...