Leaderboard
Popular Content
Showing content with the highest reputation on 09/04/2015 in all areas
-
I just wanted to share a small module I just made, that can inherit values from pages, that are higher up in the page tree. This inheritance will only show for formatted output, so for example in listers or in the frontend. When using the unformatted value one will see, if a field is really set or not. <?php class FieldtypeInheritInteger extends FieldtypeInteger { public static function getModuleInfo() { return array( 'title' => 'Inherit Integer', 'version' => 101, 'summary' => 'If empty tries to get values of the same field from further above the pagetree. This does only work for formatted values so empty values won\'t ne overwritten by editing a page.' ); } public function ___formatValue(Page $page, Field $field, $value) { if(!$this->isEmptyValue($field, $value)) return $value; $parent = $page->parent("$field!=''"); if($parent->id) return $parent->getFormatted($field); else return $value; } public function ___markupValue(Page $page, Field $field, $value = null, $property = '') { return $this->___formatValue($page, $field, $value); } }7 points
-
FieldtypeRuntimeMarkup and InputfieldRuntimeMarkup Modules Directory: http://modules.processwire.com/modules/fieldtype-runtime-markup/ GitHub: https://github.com/kongondo/FieldtypeRuntimeMarkup As of 11 May 2019 ProcessWire versions earlier than 3.x are not supported This module allows for custom markup to be dynamically (PHP) generated and output within a page's edit screen (in Admin). The value for the fieldtype is generated at runtime. No data is saved in the database. The accompanying InputfieldRuntimeMarkup is only used to render/display the markup in the page edit screen. The field's value is accessible from the ProcessWire API in the frontend like any other field, i.e. it has access to $page and $pages. The module was commissioned/sponsored by @Valan. Although there's certainly other ways to achieve what this module does, it offers a dynamic and flexible alternative to generating your own markup in a page's edit screen whilst also allowing access to that markup in the frontend. Thanks Valan! Warning/Consideration Although access to ProcessWire's Fields' admin pages is only available to Superusers, this Fieldtype will evaluate and run the custom PHP Code entered and saved in the field's settings (Details tab). Utmost care should therefore be taken in making sure your code does not perform any CRUD operations!! (unless of course that's intentional) The value for this fieldtype is generated at runtime and thus no data is stored in the database. This means that you cannot directly query a RuntimeMarkup field from $pages->find(). Usage and API Backend Enter your custom PHP snippet in the Details tab of your field (it is RECOMMENDED though that you use wireRenderFile() instead. See example below). Your code can be as simple or as complicated as you want as long as in the end you return a value that is not an array or an object or anything other than a string/integer. FieldtypeRuntimeMarkup has access to $page (the current page being edited/viewed) and $pages. A very simple example. return 'Hello'; Simple example. return $page->title; Simple example with markup. return '<h2>' . $page->title . '</h2>'; Another simple example with markup. $out = '<h1>hello '; $out .= $page->title; $out .= '</h1>'; return $out; A more advanced example. $p = $pages->get('/about-us/')->child('sort=random'); return '<p>' . $p->title . '</p>'; An even more complex example. $str =''; if($page->name == 'about-us') { $p = $page->children->last(); $str = "<h2><a href='{$p->url}'>{$p->title}</a></h2>"; } else { $str = "<h2><a href='{$page->url}'>{$page->title}</a></h2>"; } return $str; Rather than type your code directly in the Details tab of the field, it is highly recommended that you placed all your code in an external file and call that file using the core wireRenderFile() method. Taking this approach means you will be able to edit your code in your favourite text editor. It also means you will be able to type more text without having to scroll. Editing the file is also easier than editing the field. To use this approach, simply do: return wireRenderFile('name-of-file');// file will be in /site/templates/ If using ProcessWire 3.x, you will need to use namespace as follows: return ProcessWire\wireRenderFile('name-of-file'); How to access the value of RuntimeMarkup in the frontend (our field is called 'runtime_markup') Access the field on the current page (just like any other field) echo $page->runtime_markup; Access the field on another page echo $pages->get('/about-us/')->runtime_markup; Screenshots Backend Frontend2 points
-
since that app was made, PW has grown a lot, and now you could probably achieve a lot of those views in the stock admin, e.g. using lister, page tables etc..2 points
-
Schema.io provides the backend for everything shop related, everything else would be managed in processwire's backend. In the template files you'd use both api's to create the whole website.2 points
-
Craig is saying that he adds a field for each created page that contains the original id in the old database. If something goes wrong, or you need to adjust something in the import, or you need some more info from the old database that you didn't import originally, or you simply need to check if the items were imported correctly, you can check that field against the ID in the old database. $p->legacy_id = $row['id'];2 points
-
I wonder if the easiest way might be with kongondo's brand new module: https://processwire.com/talk/topic/10804-module-runtimemarkup-fieldtype-inputfield/1 point
-
UNTESTED, probably have some typos + poorly formatted code (I gotta run...) You want to add this to your template file....Wrap it around code that ensures only Superuser can load the code... //absolute path to a directory called 'tmp' within the site folder where we have our images $dir = $config->paths->site . 'tmp'; $addAndPublish = false;//true if you want to publish on save //prepare some variables we'll need later $a = 0;//for photo pages count $failed = array(); $parent =$pages->get('/portraits/'); $t = wire('templates')->get('your-portrait-template-name'); //if we found the directory (here we use Standard PHP Library (SPL)) if (is_dir($dir)) { $directory = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS); //iterate through each file in this directory foreach ($directory as $path ) { set_time_limit(30);//we try to avoid timing out //Remove and delete invalid file types $validImagesExts = array('jpg', 'png', 'gif', 'jpeg'); if($path->isFile() && !in_array($path->getExtension(), $validImagesExts)) { unlink($path);//we delete the invalid file continue; } //if valid image file we create a page named after it and later save it to the page if($path->isFile()) { //we are ready to start creating pages... $p = new Page(); $p->parent =$parent; $p->template = $t; $title = $path->getBasename('.' . $path->getExtension()); $p->title = $this->sanitizer->text($title); if (!$p->title) continue;//skip to next if no title found (just in case) $p->name = $this->sanitizer->pageName($p->title);//sanitize and convert to a URL friendly page name //check if name already taken if($p->parent->child("name={$p->name}, include=all")->id) { //if the same name already exists, add it to the $failed array [to display to user in error later] and skip to next title if ($path->isFile()) { $failed [] = $path->getFilename(); } continue; } //if add and publish is false, we save new page unpublished if (!$addAndPublish) $p->addStatus(Page::statusUnpublished); $p->save(); //add image to the page and save again $p->images_field->add($dir . '/' . $path->getFilename()); $p->save(); $a++; unlink($path);//we delete the temp file } }//end foreach //delete the tmp directory wireRmdir($dir, $recursive = true); //create a string of "failed" category titles to add to error message $failedTitles = implode(', ', $failed); //give the user some feedback... if($a > 0) echo 'Added' . $a . 'Portraits<br>'; if($failedTitles) echo 'Some Portraits not added because names already in use. These are:' . $failedTitles; }//end if (is_dir($dir))) else echo 'No Such Directory Found!';1 point
-
1 point
-
That's seems like a viable solution, but only for the active current page. You also just set the "item_current_tpl" and that's only for the current active page. If that's the idea that's ok. MarkupSimpleNavigation doesn't change any page context at all. So the $page you have there's is only the current page viewing. When working with a hook, you would have to check for current page with if($child === wire("page")) { // do your markup logic } Taking the example from the readme I linked earlier it would be like: $nav = $modules->get("MarkupSimpleNavigation"); function myItemString(HookEvent $event){ // the current rendered child page $child = $event->arguments('page'); // if current child you're viewing if($child === wire("page")) { $myimage = $child->images->getTag('icon')->width(50); $itemMarkup = "<div class='nav-icon'><img src='{$myimage->url}'></div><a href='{$child->url}'>{$child->title}</a>"; $event->return .= $itemMarkup; // send back the markup that will present a item } } // setup the hook $nav->addHookAfter('getItemString', null, 'myItemString'); // a render will then also trigger the hook above on each item echo $nav->render(); This would allow to have all different logic in your output depending on custom criterias1 point
-
Btw, thanks, your question made me realise that the code around the strip_tags need to be optimised anyway...That bit of code should only load when post small is set to true.1 point
-
Very strange indeed, I'd checked it multiple times, but copy pasting the email address back into the same input seems to have fixed it. I have no idea what happened there, but thanks for suggesting to check it once again. I have one other question regarding using this in conjunction with Ryan's Import Pages from CSV module. I have a test CSV with 4 columns (title,displayname,email,roles) and 5 rows. I'm trying to import multiple users and have EmailNewUser auto-generate a password for each, it works successfully for the first user/row, but then I get the error "No email was sent to the new user because either their email address or password was not set." after that, I'm assuming because of line 137 (although I'm not sure if it's because of the email or password being blank? Has anyone successfully managed to do this? EDIT: Seems if I include the 'pass' column, and use "" as the value for each row, it works!1 point
-
Sure, but the bottleneck is between user and internet (opera turbo servers are internet, too). So this won't speed up anything on the upload.1 point
-
class ImageBatcher extends WireData implements Module { /** * getModuleInfo is a module required by all modules to tell ProcessWire about them * * @return array * */ public static function getModuleInfo() { return array( 'title' => 'Image Batcher', 'version' => 1, 'summary' => 'Do some additional actions after uploading an image', 'singular' => true, 'autoload' => true, ); } /** * Initialize the module * */ public function init() { // do additional action after upload $this->addHookAfter('InputfieldImage::fileAdded', $this, 'createPageFromImage'); } /** * create Page from uploaded Image * */ public function createPageFromImage($event) { $file = $event->arguments(0); $parent = wire('pages')->get('/batcher'); // didn't know how to get the current page dynamically // create new page $np = new Page(); $np->parent = $parent; $np->template = 'portrait'; $np->title = $file->name; $np->save(); // page has to be saved before adding images // add image $np->image->add($file->filename); $np->save(); } } played a little around... something like this? i didn't know how to get the current page dynamically... and i didn't manage to clear the added files from the "batcher" page because i got some errors i can't explore in detail right now. this thread should also give you useful information: https://processwire.com/talk/topic/8698-creating-page-for-every-image-uploaded/ edit: i think hooking the method fileAdded should keep you save from timeouts though other things may get more complicated as it are all ajax requests (like the error i got when trying to remove all uploaded images from the batcher page) edit2: of course you would have to check if the file was added to a "batcher" page. otherwise all your image uploads will create new pages. or you could bind the module to a specific imagefield only.1 point
-
@Gazley, Must be at your end mate. I haven't encountered this nor read anywhere that strip_tags is slow. As you can see in this demo with +10K posts, showing 20 posts per summary page with a summary limit of around 500 words each and WITHOUT ProCache, PW loads the page in about 2 seconds.. http://demo.kongondo.com/blog/ And with shorter summaries: http://demo.kongondo.com/blog/posts/1 point
-
@LostKobrakai Very nice, thanks for sharing. I use similar logic in some cases but never made a module out of it... @BernhardB Let's say you have an element in your page header that echoes a text field and that element is mandatory. Most of the times your element will show a text related to the current category, but you want to be able to overwrite that text in some cases on some child pages. With this module (well, with a Text version of it instead of Integer) you can do that easyly and even see it in admin.1 point
-
Imagine following page-tree with a field "speed" on all pages: - beachbuggies ( speed = 50 ) - buggy #1 ( speed = '' ) - buggy #2 ( speed = '' ) - buggy #3 ( speed = 60 ) Now buggy #1 and #2 will show a (max) speed of 50, which is inherited from the beachbuggies group, while buggy #3 will show a speed of 60.1 point
-
Wouldn't you need to save the repeater like this? $channel->save('tvChannelGrid'); // Save the repeater. that's how i usually save fields; not sure if you would need to save the page after that.1 point
-
There are various topics in the forum about importers. The only thing different in really "mass" creation is that you need to be aware of timeouts / memory_limits and batching things before you hit them.1 point
-
Thank you for your reply and of course your module. You were right, after recreating the field, everything works just fine.1 point
-
In tutorials section of the site we are encouraged to request tutorials. This topic is such a request. ProcessWire is advertised to be great in not only building unique frontend features, but also custom backends. I would be very much interested in tutorial on building a custom backend for a site. I put some questions that could be covered in such a tutorial in a list below: How to create custom backend pages How to change the default starting page for admin users How to serve different backends to different users depending on roles How to style the page tree (I have seen it styled in some Soma's video tutorials, so it is a request for a best practice or an advice) Anything else you think appropriate here))) If someone could generously provide a screencast of a custom backend workflow it would be great tutorial in itself.1 point
-
the mentioned office application is nothing else than a custom frontend - PW original backend is only used for storing the data....so no limts on this side. for the PW backend there are much options on this so you could go: 1. using Lister Pro for creating dashboard like pages in the admin for special selectors you preselect or let the user select... 2. using AdminCustomPages module to simply have the option to work with a "normal" page template in the backend and don't have to code a Process module 3. other options that work but i don't know now....since i've learned that in PW there is always a another way... take a look at existing modules if you wanna learn something about the backend - you could take a look at: - Soma's imagemanager - Code examples from LostKobraKai about using datatable module on own admin pages - renobird's example of a simple process module for admin pages search the forum regards mr-fan1 point
-
I'm not sure where you see that WP is so much different than PW from the user perspective, except maybe the initial Dashboard. It's neither themed like the frontend nor can it provide more or less than what's possible in pw and people seem to love it. The posts / site listings can be mimiced by listers, the plugin and settings area shouldn't be visible to less-permissioned users anyway (our fields/templates). The Design area is simply a page somewhere in the pagetree (there are various ways to get this special page in the navigation). I'm not sure about comments, as I don't use them, but I doubt them being handled overly different. Users are already shown in a lister. There's a page to edit the own profile. Additionally you can really limit your users down to the bare minimum they need to see. Hide pages, they don't need to edit, or even hide the whole pagetree and let them only use listers. There's a lot you can do there. And if some feature is really not present (dashboard) you can still look in the modules directory or code an own process module to resemble your needs. By now I did lot's those custom process modules and it's really not that hard to integrate own layouts / views into the backend with the cohesive styling it's providing. If you find that users cannot grasp what their changes in the backend do in the frontend there are various examples where people integrated the fredi module or links to the backend. So people can decide in the frontend what they want to edit and from there go directly to edit it in the backend interface. When talking about "casual" users, who can edit their own content, but shouldn't know about a backend – that's 100% frontend – at least in my opinion. It's part of the service you offer the user. This can look like a "backend" but you're really better of not trying to integrate that in any way with the real pw backend. Just code it like any other frontend-part and use the api. Think about when the code of the backend would change a bit. Suddenly your users cannot edit their content anymore. That's not what you want.1 point
-
There is one thing that needs a bit attention when importing data. We need to check if a record was also imported by a previous run of the script, otherwise you may end up with numerous duplicates. while($row = $result->fetch_assoc()) { $title = wire("sanitizer")->text($row['title'], array("maxLength"=>128)); // fetch and sanitize the title $p = wire('pages')->get("title={$title}"); // check if it already exists if(0 < $p->id) continue; // move on to the next record if it exists $p = new Page(); $p->template = "dam"; $p->parent = "something"; $p->title = $title; // use the sanitized title $p->save(); }1 point
-
I saw that Hangout, and even if it's always interesting whatever discussions around CMS's, that time it was not much "new" or even worthing to dig more about. The event conclusion: The best CMS is ... the one who fits with your project/needs/team/etc. That's it.1 point
-
$myDB = new Database('localhost', 'username', 'password', 'db_name', 3306); $result = $myDB->query("SELECT * FROM my_table"); while($row = $result->fetch_assoc()) { $p = new Page(); $p->template = "dam"; $p->parent = "something"; $p->title = $row['title']; $p->save(); }1 point
-
@uiui - Sorry you are having troubles. I just tested and the only allowed filetype is mp4 (which is correct). I am wondering if perhaps you converted an existing files field to a video field? If so you will need to manually change the valid file extensions on the Details tab. Either that or add a new Video field from scratch. Please let me know if that takes care of things for you. Also, please note that the built-in ->play() uses the Sublime video player which is no longer available. At some point I may add built-in support for another (or several) players, but for the moment you will need to provide the player for the front-end yourself. I like http://mediaelementjs.com/ but there are lots of others.1 point
-
@ro-bo thanks for the detailed instructions. I was able to duplicate it after following the steps in those instructions. And @LostKobrakai was right that there was a missing getBlankValue in the sanitizeValue method. This week's PW dev version will include this fix at the top of sanitizeValue: if(empty($value)) return $this->getBlankValue($page, $field);1 point
-
Seems like it, but there are few other unresolved ones in that function as well right now.1 point
-
I'm having a slight issue on the lastest PW build 2.6.13. I can create a crop but it won't display on the front end.1 point
-
This look veeeery interesting! https://schema.io/ Notice the PHP docs here https://schema.io/docs/clients Edit: This tool is API centric —seems to work a bit like GatherContent and the likes— The platform holds all the needed info and takes care of operations, but doesn't seem to have any front end, and offers instead a nicely built API to allow integration on any framework.1 point
-
I am pleased to announce the full release of version 2.6.1 of the language pack. (Versioning now moving to match that of PW itself) I setup a new module info section in the modules repo. http://modules.processwire.com/modules/french-language-pack/ (might not be live as you read this) Also, the GitHub repo is moving to https://github.com/plauclair/pw-lang-fr/. Head over to the releases page for download https://github.com/plauclair/pw-lang-fr/releases. Enjoy!1 point
-
hi ivan, very good request! what about a showcase thread or sub-forum for backend modifications? i would also be interested in soma's video you were talking about. do you have a link for that please?1 point
-
I don't think it's about the admin theme, but how to create custom pages (process modules) and to cater a advanced backend for clients.1 point
-
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
-
OH. MY. GOD. Tell me I'm not dreaming! I've been scouring the Internet for weeks looking for a CMS that would allow me to give my clients a decent user experience. I've developed sites in Joomla, Drupal, Silverstripe, and Wordpress. Joomla was a nightmare of epic proportions. The admin interface is confusing and inconsistent. Drupal had promise in theory, but trying to customize it just caused it to bug out. Silverstripe is nice, but the admin panel is slow and clunky. Wordpress is great for blogs, but feels like you're hacking it if you're trying to use it for more than that. After my frustrations with the big name CMSs, I decided to try and get a better lay of the land and see what else was out there. Every one of them was either lacking a crucial feature, poor on the usability front, or a buggy mess. I have some programming knowledge, but mainly I am a designer looking for a CMS that lets me design the client's experience in the same way that I'm able to design the end user's experience. I don't need templates or templating languages. I want something that lets me be the designer and then gives my clients the power to work with what I've developed. As someone deeply concerned about user experience, I don't understand it. How could so many developers get this so wrong? Were they just throwing these things together without thinking about the use cases? The need for flexibility? What people actually NEED in a CMS? I was beginning to think I'd have to become a PHP developer and build something from scratch. So far, what I see in ProcessWire is almost exactly the ideal CMS I have been piecing together in my mind's eye. The consistency in the mental design model, the absolutely crucial ability to create your own page types and custom fields for your clients which is utterly lacking or nonexistant in nearly every major CMS, the ability for logged in users to easily update a page they're on by simply hitting an "edit" link from the front end... Elegant, logical, and flexible. It's obvious that you've put a lot of thought into this. Thank you. I can't wait to get started.1 point