Leaderboard
Popular Content
Showing content with the highest reputation on 09/07/2017 in all areas
-
https://moz.com/blog/structured-data-for-seo-1 Seems to me that one of the huge advantages we have with PW is that it's fairly straightforward to include 'extra' stuff as part of our rendered markup, without necessarily waiting for someone else to develop a plugin of some kind, as is so often the case with other CMSs. This blog post (and Pt 2 when it comes out I expect) not only goes into some depth on its subject, but links to loads of other resources on the topic.5 points
-
URLs are generated from page names by default. What I'm trying to show is that you dont have to use IDs for page names, otherwise when generating URLs you'd get /codes/123 instead of /codes/wall-mounted-light. Below is a sample template that should work in your case <?php namespace ProcessWire; /** @var Pages $pages */ /** @var Sanitizer $sanitizer */ /** @var Page $child */ $child = null; if ($input->urlSegment1) { $codeChildName = "code--{$input->urlSegment1}"; $codeChildName = $sanitizer->pageName($codeChildName); $child = $pages("parent=$page, name=$codeChildName"); if (!$child->id) throw new Wire404Exception(); } ?> <div class="code-page"> <?php if ($child): ?> <!-- RENDER CHILD PAGE --> <h1>Child: <?= $child->title ?></h1> <?= $child->body ?> <?php else: ?> <!-- RENDER CODE PAGE WITH CHILDREN --> <h1><?= $page->title ?></h1> <?= $page->body ?> <h2>List of children:</h2> <ul class="chilren"> <?php foreach ($page->children as $c): ?> <?php $childUrl = "./$c->name"; // generate urls for the child ?> <li class="child"> <a href="<?= $childUrl ?>"><?= $c->title ?></a> </li> <?php endforeach; ?> </ul> <?php endif; ?> </div> I also concur with @bernhard. If you're modifying the core behavior and rendering child page at the url that matches the child's, then I'd say you should reconsider your approach. You're only complicating it for yourself. Also, thanks, I'm doing what I can. These questions are like quizzes that I enjoy solving, and get to practice coding with PW.3 points
-
if($page->template == "contact" || $page->template == "course" || $page->template == "holiday" || $page->template == "newsletter") { if(!$page->is(Page::statusHidden)) include ("contact.inc.php"); } Check contact.inc.php Might be a field on the homepage.2 points
-
Others may come up with different suggestions, but intercooler.js (there is a module, but I haven't used it, even though I have used intercooler) can do infinite scrolling and history.pushstate. (And a load of other clever stuff, but they're your main two.)2 points
-
@cjx2240, I'm not sure if this is the best way, but hey it works. Put this in your /site/ready.php wire()->addHookBefore('Page::render', function (HookEvent $e) { // requested page (not the one being rendered) $page = $e->page; // skip admin pages if (strpos($e->input->url, $e->config->urls->admin) === 0) return; // force redirect to homepage if ($page->id != 1) { $e->session->redirect('/'); } }); And in your home template, put this at the beginning <?php namespace ProcessWire; $splashPage = $pages->get(1353); echo $splashPage->render(); return $this->halt();2 points
-
Thanks, I've added these mods (haven't tried though): https://github.com/rolandtoth/CroppableImage3/commits/master I can create a PR if everything's OK.2 points
-
Works great. One small fix from me. After saving the page or revisiting it I'm seeing a previous cached icon-size thumb. This happening even on localhost. So, I fixed it by replacing the 155 line on InputfieldCroppableImage3.module with this: $out .= $warning ? '' : " <img src='{$pagefile->getCrop($suffix)->height(48)->url}?nc={$pagefile->mtime}' alt='' />"; // added a small indicator thumb to each crop-button2 points
-
Yes, crop orientation is the culprit (added via $config->imageSizerOptions array, at least that's how I could see it). I've made the fixes here, please try: https://github.com/rolandtoth/CroppableImage3 This should be a generic fix, I pass the image url from the crop review modal to the js. Additionally, only the current preview btn image is refreshed and not all (unlike before).2 points
-
I think it's important to sync `jobs` `currentJobs` fields such that `currentJobs` doesnt have more jobs than `jobs` field has. You can use the following hook to update `currentJobs`. wire()->addHookBefore('Pages::saveReady', function (HookEvent $event) { $page = $event->arguments('page'); if ($page->template != 'person') return; // make sure currentJobs items <= jobs items if ($page->isChanged('jobs')) { foreach ($page->currentJobs as $cj) { if (!$page->jobs->has($cj)) $page->currentJobs->remove($cj); } } });2 points
-
I'd still keep the Page fields, and use two fields to determine all jobs and current jobs a person has. What you should do is to pull up your sleeves and get down to build a select field, maybe extend @ryan's asmSelect, or more modern alternatives (chosen.js select2.js etc) that have tagging support. In fact, I had some free time and went ahead and created a signup form that does this. I used selectize.js for selecting/creating jobs on the frontend. Options load with AJAX at page load Once you submit the form, you get this On the backend, I get the input, sanitize it and filter values, if a new job name is specified, I create the page, and add it to `jobs` or `currentJobs`. Here's the code in all its glory: <?php namespace ProcessWire; /* @var $input WireInput */ /* @var $sanitizer Sanitizer */ /* @var $pages Pages */ if (input()->requestMethod('GET') && input()->urlSegment1 === 'jobs.json') { // serve json for input fields $jobs = pages('template=profession, sort=title'); echo wireEncodeJSON($jobs->explode(['id', 'title'])); return $this->halt(); } elseif (input()->urlSegment1) { // don't allow other url segments throw new Wire404Exception(); } elseif (input()->requestMethod('POST')) { $jobs = $input->post->jobs; $currentJobs = $input->post->currentJobs; // sanitize inputs $saneJobs = []; foreach ($jobs as $j) { $jobId = $sanitizer->int($j); $jobTitle = $sanitizer->text($j); if ($jobId) $saneJobs[$jobId] = false; // not current job elseif ($jobTitle) $saneJobs[$jobTitle] = false; // not current job } // check if a job is one of current jobs foreach ($currentJobs as $cj) { $currId = $sanitizer->int($cj); $currTitle = $sanitizer->text($cj); // if person has the job, set it to current if (isset($saneJobs[$currId])) $saneJobs[$currId] = true; // set to current elseif (isset($saneJobs[$currTitle])) $saneJobs[$currTitle] = true; // set to current } // save info $person = $pages->get(1228); $person->of(false); $addableJobs = []; $addableCurrents = []; foreach ($saneJobs as $j => $isCurrent) { // $j is job id or new job title $job = null; // does job already exist? if (is_int($j)) { $job = $pages->get($j); } else { // create a new job and add it $job = new Page(); $job->template = 'profession'; $job->parent = 1027; $job->name = $sanitizer->pageName($j); $job->title = ucwords($j); // already sanitized, but capitalize it $job->save(); } if (!$job) continue; $addableJobs[] = $job; if ($isCurrent) $addableCurrents[] = $job; } $person->jobs->add($addableJobs); $person->currentJobs->add($addableCurrents); $person->save(); } ?> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.4/js/standalone/selectize.js"></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <link rel="stylesheet" href="https://rawgit.com/selectize/selectize.js/master/dist/css/selectize.bootstrap3.css"> <div class="flex"> <form method="POST" class="signup-form"> <div class="form-group"> <label for="name">Name</label> <input name="name" required type="text" class="form-control" id="name" placeholder="Name Lastname"> </div> <div class="form-group"> <label for="jobs">Your Jobs</label> <select name="jobs[]" required id="jobs" multiple class="form-control"> <option>Loading...</option> </select> </div> <button class="btn" type="submit">Save</button> </form> </div> <script> $jobs = $('#jobs'); // fetch all jobs $.getJSON('./jobs.json', function (data) { initSelect(data); }); function initSelect(data) { const makeOption = (it) => ({text: it.title, value: it.id}); let selected = data.splice(0, 5).map(makeOption); window.jobs = data; $jobs.selectize({ create: true, render: { item: function (data, escape) { return `<div class='option'> <span class="option__label">${escape(data.text)}</span> <input class="option__current" type="checkbox" name="currentJobs[]" value="${escape(data.value)}"> </div>`; } }, options: data.map(makeOption), // populate the field with the jobs person already has // items: data.splice(1,4).map(i => i.id) }); } </script> <style> .flex { display: flex; justify-content: center; margin-top: 10rem; } .signup-form { width: 500px; margin: auto; } .option { width: 100%; } .option:after { content: ''; display: table; } .option__label { float: left; } .option__current { float: right; } </style>2 points
-
This answers my question. The site is updated regularly it seems. Are you able to convince them to have it ported to PW then? They'd pay (in my reckoning) a pretty much one-off amount to have everything under one roof with little or no monthly running costs. But I digress. Back to your question, some of your choices are: i) access the database directly (ii) use the JSON feeds as suggested by @dragan. The challenge with (i) is that the data you need seems to be in different tables (e.g. the groupings are probably their own table versus the registry of animals itself). That would mean writing some SQL to get what you need. Also have a look at their Views Datasource. (iii) Use their REST/SOAP we services, e.g. RESTful, RESTFul Web Services and Services. I would go for option (iii). There's a number of videos and articles out there to get you started. This one is a good intro as well as the video here. On ProcessWire side, since you are not accessing its database, one option would be to create virtual pages on the fly to display your data. This is only if you need access to PW API. It is just one option and there will probably be other ideas. However, for something as simple as displaying a table with the number of animals in some group, you can even use plain old PHP to loop through the data and create a table. I am just brainstorming. I haven't done anything like this before so take my rumblings with a healthy pinch of 'salt' . Whatever your choice of action, you'd need to get your hands dirty with this one.2 points
-
Just wanted to share what I recently used to create forms in modules and in frontend using the API and Inputfield modules PW provides and uses on its own. I think many newcomers or also advanced user aren't aware what is already possible in templates with some simple and flexible code. Learning this can greatly help in any aspect when you develop with PW. It's not as easy and powerful as FormBuilder but a great example of what can be archieved within PW. Really? Tell me more The output markup generated with something like echo $form->render(); will be a like the one you get with FormBuilder or admin forms in backend. It's what PW is made of. Now since 2.2.5~ somewhere, the "required" option is possible for all fields (previous not) and that makes it easier a lot for validation and also it renders inline errors already nicely (due to Ryan FormBuilder yah!). For example the Password inputfield already provides two field to confirm the password and will validate it. De- and encryption method also exists. Or you can also use columns width setting for a field, which was added not so long ago. Some fields like Asm MultiSelect would require to also include their css and js to work but haven't tried. Also file uploads isn't there, but maybe at some point there will be more options. It would be still possible to code your own uploader when the form is submitted. Validation? If you understand a little more how PW works with forms and inputfields you can simply add you own validation, do hooks and lots of magic with very easy code to read and maintain. You can also use the processInput($input->post) method of a form that PW uses itself to validate a form. So getting to see if there was any errors is simply checking for $form->getErrors();. Also the $form->processInput($input->post) will prevent CSRF attacks and the form will append a hidden field automaticly. It's also worth noting that processInput() will work also with an array (key=>value) of data it doesn't have to be the one from $input->post. Styling? It works well if you take your own CSS or just pick the inputfields.css from the templates-admin folder as a start. Also the CSS file from the wire/modules/InputfieldRadios module can be helpful to add. And that's it. It's not very hard to get it display nicely. Here an code example of a simple form. <?php $out = ''; // create a new form field (also field wrapper) $form = $modules->get("InputfieldForm"); $form->action = "./"; $form->method = "post"; $form->attr("id+name",'subscribe-form'); // create a text input $field = $modules->get("InputfieldText"); $field->label = "Name"; $field->attr('id+name','name'); $field->required = 1; $form->append($field); // append the field to the form // create email field $field = $modules->get("InputfieldEmail"); $field->label = "E-Mail"; $field->attr('id+name','email'); $field->required = 1; $form->append($field); // append the field // you get the idea $field = $modules->get("InputfieldPassword"); $field->label = "Passwort"; $field->attr("id+name","pass"); $field->required = 1; $form->append($field); // oh a submit button! $submit = $modules->get("InputfieldSubmit"); $submit->attr("value","Subscribe"); $submit->attr("id+name","submit"); $form->append($submit); // form was submitted so we process the form if($input->post->submit) { // user submitted the form, process it and check for errors $form->processInput($input->post); // here is a good point for extra/custom validation and manipulate fields $email = $form->get("email"); if($email && (strpos($email->value,'@hotmail') !== FALSE)){ // attach an error to the field // and it will get displayed along the field $email->error("Sorry we don't accept hotmail addresses for now."); } if($form->getErrors()) { // the form is processed and populated // but contains errors $out .= $form->render(); } else { // do with the form what you like, create and save it as page // or send emails. to get the values you can use // $email = $form->get("email")->value; // $name = $form->get("name")->value; // $pass = $form->get("pass")->value; // // to sanitize input // $name = $sanitizer->text($input->post->name); // $email = $sanitizer->email($form->get("email")->value); $out .= "<p>Thanks! Your submission was successful."; } } else { // render out form without processing $out .= $form->render(); } include("./head.inc"); echo $out; include("./foot.inc"); Here the code snippet as gist github: https://gist.github.com/4027908 Maybe there's something I'm not aware of yet, so if there something to still care about just let me know. Maybe some example of hooks could be appended here too. Thanks Edit March 2017: This code still works in PW2.8 and PW3.1 point
-
thanks a lot. I wouldnt have got it by now. Must have taken another night without you guys!!!!!!!!!!!!!1 point
-
1 point
-
1 point
-
1 point
-
1 point
-
1 point
-
@henri You shouldn't need to search the db. You need to figure out where the code that sends the emails is and work from there. Find the page with the form in the page tree and find its template. Then look at the template file for the code responsible for sending the emails.1 point
-
I've made a few CSS changes, now preview buttons look the same in all 3 image grid modes and I've fixed some other glitches too. The CSS file was a bit overcomplicated, I could eliminate large blocks so I hope I haven't broken anything I also checked the preview images with more than one crop setting (multiple buttons) and it's working fine here. All is in the PR.1 point
-
If pages are not public, then you wouldnt need to worry about SEO. Page name format works for pages created in the future, but for the current pages you can do: $codes = wire()->pages('template=code'); foreach ($codes as $c) { // wall-mount-mini-split becomes code--wall-mount-mini-split $replacement = "code--{$c->name}"; $c->of(false); $c->setName($replacement); $c->save(); } And in your template, where you check urlSegments: if($input->urlSegment1) { if ($input->urlSegment2) throw new Wire404Exception(); $codeName = "code--{$input->urlSegment1}"; $codeName = $sanitizer->pageName($codeName); $code = $pages("template=code, name=$codeName"); if(!$code->id) throw new Wire404Exception(); // do sth with the code page }1 point
-
1 point
-
How about using a counter or a flag to signal that the page has already been rendered? If you store the rendered output in the first iteration then you can just output it later on without calling render again. Or am I misunderstanding something?1 point
-
1 point
-
No problem. I've used it quite a bit, although not for the kind of stuff you're talking about at the moment. For anyone like me who's a bit sketchy on plain js, it's a great help and very powerful. It makes doing ajaxy search boxes and such an absolute doddle, cos PW plays real nice with ajax requests.1 point
-
Hey @abdus, hope you're well. Thanks also for pointing me to these great javascripts I also didn't know about these little beauties either. I now have plenty to work with. I appreciate the heads-up! Cheers!1 point
-
What you need to do is basically: Get a list of pages with limit for pagination $pages->find('template=post, limit=10, sort=-published'); Render a list of items Render the pagination at the bottom of the page Using JS listen to scroll event, and trigger AJAX request Get the response, filter the list of elements, replace the container that holds the old items Repeat As a different approach, you can implement barba.js for PJAX (ajax with history support) (and limit it to only pagination links, if you prefer), and trigger click() on the next pagination link when it gets closer to viewport. For that you can use scrollMonitor.js. Total weight of these two scripts are under 15KB, they don't add much load on the page. One bonus is that you can enable barba.js on all (internal) links (it's enabled by default) to make transitions between pages feel more responsive.1 point
-
During boot, inside /site/config.php $page is not yet determined, so I am limited in determining what to prepend/append. For this reason I disable $config->prependTemplateFile (& append) and use a 'controller' hook like this. wire()->addHookBefore('TemplateFile::render', function (HookEvent $event) { // skip if AJAX if ($event->config->ajax) return; /** @var TemplateFile $templateFile */ $templateFile = $event->object; // Skip admin pages if (strpos($event->input->url, $event->config->urls->admin) === 0) return; // check if this is a partial template, if so, stop $fileDir = pathinfo($templateFile->get('filename'), PATHINFO_DIRNAME); $templatesPath = $event->config->paths->templates; if (realpath($fileDir) !== realpath($templatesPath)) return; $templateName = $templateFile->page->template->name; $prepends = [ 'routes' => $templatesPath . "{$templateName}.routes.php", 'before' => $templatesPath . "_before.php" // like your _init.php ]; // then comes the actual template file $appends = [ 'view' => $templatesPath . "views/{$templateName}.php", 'after' => $templatesPath . "_after.php", 'layout' => $templatesPath . "layouts/main.php" ]; foreach ($prepends as $name => $file) { if (file_exists($file)) $templateFile->setPrependFilename($file); } foreach ($appends as $name => $file) { if (file_exists($file)) $templateFile->setAppendFilename($file); } }); So to adapt this to your setup, you can define your urlSegments inside template.routes.php, and stop further appends with return $this->halt(). This way only template.routes.php is loaded and remaining appends are skipped while still allowing PW to complete its shutdown.1 point
-
1 point
-
@horst @tpr After closing the modal the hover tooltip appears and it feels like a glitch cause it also half-cover the button preview. I fixed it by replacing the 34 line on InputfieldCroppableImage3.js with this: items: 'a:hover' Thank you very much guys for your work1 point
-
1 point
-
There's always good old pagination you could use... https://processwire.com/api/modules/markup-pager-nav/ Maybe in 2017 that's not considered "sexy", but hey - if it works, it works1 point
-
Hi @taoguang. Welcome to the forums. For such custom markup, I recommend you make use of the getMenuItems() method as described in this post. I am happy to assist if you do get stuck.1 point
-
As a follow-up: I had the same problem and tried everything – including the one recommended by prestoav. I am developing 3 sites running on a FastHosts reseller account. All running PW 3.0.42 on php 5.6. One is on an older server and had no problems uploading images. The other two are on their newer 'cluster' servers and those were the sites with problems uploading. Long story short, the FastHosts support investigated and said that turning debug OFF in the site config file would solve the problem. Sure enough, that's fixed the problem. I don't know if it is a problem with PW, FastHosts servers or what, but if anyone encounters this problem, it's worth checking your debug setting.1 point
-
@justb3a: I had this idea before, but it is not really convenient for users (editors) and looks a bit "half-baked". I think this feature should really go into core, as you suggest. Thank you. Edit: Feature request: https://github.com/processwire/processwire-requests/issues/1171 point
-
@PWaddict: There is already a module for cropping images: CroppableImage3 @theo: Actually the core/module do support it in a way that's not immediately obvious or as straightforward as checking a checkbox, but it's still quite useful and worthwhile when you need the capability. You could use tags or any other image extra field to achieve this. Edit any existing files/images field, or create a new one. When editing the field settings, check the box to enable “tags”, or create a new extra field named “hidden“ or something like that. In the “tags” or “hidden“ field for the image (which should be unpublished), enter a specific phrase like “hidden" (tags field) or “1“ (hidden field). Now you need to check if the file should be shown on the front-end of your site. Here's how you do that: // tag usage example if (!$image->hasTag('hidden')) echo "<img src='$image->url' alt=''>"; // extra field hidden example if (!$image->hidden) echo "<img src='$image->url' alt=''>"; You could also send a feature request to add this as a core feature (so you do not need to check manually whether the image is hidden and as an extra bonus the hidden images would be visible at first glance using opacity to indicate that).1 point
-
Sorry guys. Buried with work currently, but it is getting better. Padloper is alive and kicking!1 point
-
1 point
-
1 point
-
1 point
-
1 point
-
Hi @Robin S - just wanted to confirm that you have everything working as expected now when editing Page Tables in a modal window? @tpr - I am a little confused - what styles.css file are you talking about? The one included in TracyDebugger, or something else? How can you use this module in non-PW projects? @hettiger - glad you like it - thanks! PS Hi again everyone - it's been a while1 point
-
I have a text area on a template that stores html. I think it would be a great addition if we could make the page save feature keep my spot in the text area and keep the spot of the scroll bar instead of taking me to the top of the page and starting the text area at the top. (I could have been editing a line in the middle of the box and then need to scroll down to find it again to make another change) Even better would be an update that wouldn't refresh the whole page at all but just the field contents! But a flash is ok as long as it remembers where I was at!1 point
-
Just in case someone else wants to do something like this, I just wanted to note that hooking before ProcessPageEdit::execute and foreaching through wire('breadcrumbs') works well. But for my particular needs I had problems with this approach and the Reno theme and so had to take another approach which worked: hooking after Process::breadcrumb Anyway, thought it might be useful info for others.1 point
-
I was just reading this post on how to make breadcrumbs in ExpressionEngine (http://bit.ly/hello_crumbly) and how you basically have to resort to a plugin to do it for you. It reminded me of some of the problems I had with EE when I was trying to use it and develop in it awhile back. One being that URLs aren't a primary/unique key to a page, natively in the system. Imagine ProcessWire with only it's root level pages and url segments, and that gives you an approximation of what you have to deal with in EE. I'm not saying it's wrong, but it's a different approach, and one that I found frustrating to work with. The post also made me realize I didn't have anything on the site demonstrating how you might make a breadcrumb trail (other than in the default site profile). It's really a simple thing (at least, relative to EE), so figured I'd post here. : <?php foreach($page->parents() as $parent) { echo "<a href='{$parent->url}'>{$parent->title}</a> "; } You may want to output those as list items or add some >> signs between items, but as far as code goes, the above is all there is to it. What it's doing is just cycling through the current page's parents till it reaches the homepage. Lets say that you also wanted to include the current page in your breadcrumb trail (which is probably redundant, but some sites do it). What you'd do is just append it to the list of parents that gets cycled through: <?php foreach($page->parents()->append($page) as $parent) { echo "<a href='{$parent->url}'>{$parent->title}</a> "; } The reason this is so simple is because every page is anchored to a specific URL and those URLs are representative of the site's actual structure. Pages in PW are actually uniform resource locators (URLs) whether on the front end or the back end. Because of that, there's no guesswork or interpretation to be done. In EE and Drupal (as examples) that data does not natively live at a specific URL. Instead that data is off in a separate container and the ultimate URL (or URLs plural) where it lives–and will be presented by default–are not as cut and dry. Yes, you can install plugins or go through strange taxonomic/channelistic/cryptic trials to approximate the behavior in other systems. And yes you can use url segments and pull/render any other pages you want in ProcessWire according to your own needs. But I'm talking about the fundamental and core system differences. This difference goes far beyond simplicity of generating breadcrumbs–it is a factor that translates to simplicity throughout the API.1 point
-
You can get user created and modified for a page with <?php $page->createdUser->name; $page->modifiedUser->name; $page would be $child in your code1 point