Jump to content

BitPoet

Members
  • Posts

    1,331
  • Joined

  • Last visited

  • Days Won

    61

Everything posted by BitPoet

  1. Each students and pets table is its own template, each row a page. For a limited set of repeating information like your pet type, a page reference field is most often the way to go. The "pet type" pages can live anywhere in the page tree and can optionally have a PHP template to list all pets of that type. The pets-students relationship can be defined as a page reference field linking from the pet to the student, from the student to the pet or, if you plan to implement complex selectors later on, using the Connect Page Fields module also both ways. The page reference fields can be limited to only show pages with a certain template (student, pet, pet type). Names and age should be regular (text, integer) fields. You could re-use the mandatory title field for the student and pet name and just change the label for it in the individual template's settings. I'd probably also create a "pets" and "students" template (plural) under which the individual entries live in the backend to keep things sorted and allow outputting lists. Home (or another common parent page) Students Heather Rachel ... Pets Rex Thomas ... Pet Type Dog Cat ...
  2. I sent him a PM to be sure, new threads and mentions are probably easy to miss.
  3. And here's another update: I worked on history support. Now each time a document is saved, the "old" version is saved away (under site/assets/cache/.loolversions/$pageid/ with a unique hash as a name) and an entry is added into the versioning table (lool_versions). These versions can already be listed in the UI (File -> Version history opens a sidebar at the right). Also, the UI language for the editor can now be set in PW's profile settings. If you feel daring, you can grab the latest dev branch and take it for a test drive.
  4. Looks like there is an include:sendgrid.net missing in processwire.com's SPF record. Your mail admin might be able to add an exception for processwire.com, but since there is a hard fail policy in the record, @ryan needs to change that in his DNS (or perhaps he has already done so but the change hasn't propagated to the DNS servers yet).
  5. Update: I finally found a little time to work on the module. The WOPI endpoint (the "server" script that LibreOffice Online talks to) is now created by the installer. Also, there was a bug when PW was installed in a subdirectory, which has been fixed. The next thing I'm going to work on is revision history. This is quite underdocumented territory, so hold your fingers crossed ?
  6. Somehow your reply slipped by me. In case it's not too late: docker really is fairly simple if you have a reasonably current platform. I have it running on Ubuntu 16 inside a VirtualBox VM and on a recently installed Ubuntu 18.04 server. In both cases, it's little more than running "apt-get install docker". To get Collabora CODE installed and running is quite smooth too, though you have to look out for one or two pitfalls. The first step is always running "docker pull collabora/code". This command should get things up and running on the CODE side: server_name=my.host.name docker run -t -d -p 127.0.0.1:9980:9980 -e "domain=my\\.host\\.name" --restart always --cap-add MKNOD --add-host="my.host.name:my.hosts.ip" collabora/code Of course you have to replace my.host.name with your official hostname (same for my\\.host\\.name) and insert your real IP instead of my.hosts.ip. For the latter, use the internal IP if your server is behind a NAT. Any environment settings ("-e" arguments) are usually documented in the docs for the individual docker image. Docker will take care of restarting your container when the docker service itself is spun up. You can see your running docker containers by typing "docker ps". The output will end with some fancy name (like "lucid_pike") here: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1e5ed6b3922c collabora/code "/bin/sh -c 'bash st…" 18 hours ago Up 18 hours 127.0.0.1:9980->9980/tcp lucid_pike This name is used to point other docker commands to the instance. You can e.g. view its logs with "docker logs lucid_pike".
  7. That's a limitation. The URL field type (which AssistedURL adds JS sugar to) doesn't store a page id for underlying URLs, so it has no way of knowing that the link is an internal one and that the URL has changed. I solved that problem by using a radio button that switches between the URL field (for non-PW links) and a regular page field. That's not perfect, but it works so far (quite a few years by now, actually) and I haven't had any user complaints.
  8. Not really. Since you have to store the value, you also need to load the page it is attached whenever you want to read it. PHP provides no built-in persistent storage that reaches across sessions and is shared between individual threads/processes. If you want to avoid the overhead of loading $homepage and the counter for every page view, you could however use a memory cache like memcached or APC, update that when the counter is incremented and read from it when viewing other pages (or fall back to $homepage if its not yet set, e.g. after a server restart).
  9. Just for display in the repeater label? That can be done with a little hook in site/ready.php: wire()->addHookAfter('InputfieldRepeater::renderRepeaterLabel', function (HookEvent $e) { $label = $e->return; $page = $e->arguments('page'); $label = preg_replace_callback('/\\[([a-zA-Z0-9_]+)\\]/', function($match) use($page) { return number_format($page->{$match[1]}, 2, ',', '.'); }, $label); $e->return = $label; }); This looks for a field name between square brackets (something like "[price]" in "#n: {title} - {item} - [price]") in the label and replaces it with the formatted value of that field.
  10. Boolean logic says that, if you want to negate a compound term, you need to negate each individual term AND change the operator. This is commonly referred to as De Morgan's law. !(a and b) = !a or !b Thus, you only need to change the and to or in your if clause: <?php if($item->size->value != 'half' || $item->prev()->size->value != 'half'): ?>
  11. If I recall right, I read a discussion or issue about mystyles.js always pointing to the core file in the backend some time ago. The solution there I think was to replace the word mystyles with something different, e.g. renaming site/modules/InputfieldCKEditor/mystyles.js to customstyles.js and using a different name as prefix in the inputfield settings: customstyles:/site/modules/InputfieldCKEditor/customstyles.js Of course, the name change also needs to be done inside customstyles.js: CKEDITOR.stylesSet.add( 'customstyles', [ // style definitions ] );
  12. You can iterate over the fieldgroup property of the page's template. Edit: perhaps starting with a copy of the general page and emptying/setting a few fields that need to be different might also be an option.
  13. Hope everything heals smoothly, Ryan, and that they'll find nothing wrong in the x-ray. Sounds like you just scraped past a sepsis, which would really have been no fun, so it's good to hear you went to the doctor in time.
  14. IMHO not without AJAX (which breaks SEO). The (first) request for the page's html happens before any information about screen sizes can be transferred. If it's just images you could: use placeholder images and delay load the correct images through js -> this runs the danger of multiple redraws of the page, which can get really ugly with slow connections or go in the direction adaptive images does by setting a cookie with the screen width (or a resolution indicator) and route all page image requests through a custom PHP script that determines the exact image to serve -> harder on the server and its memory and likely to break caching strategies I'm generally not happy with such approaches, as even screen width (not just browser width) is a dynamic value. The moment I rotate my phone/tablet/monitor, it changes. In the middle of browsing. So to me, adaptive content is mostly a client side problem that needs a client side solution. AJAX loading the relevant content is a possibility where SEO is not a goal (like in corporate intranets).
  15. I wouldn't do that for a background video. It might get blocked in other countries for no reason at all (like the silence in the movie sounding exactly like the silence in a popular movie which Disney has copyrighted) without you/your client noticing. I'd either host it myself (in this case the server or a script should support partial transfers) or use a CDN.
  16. Perhaps FieldtypeDropdownDynamic might be worth a look. It shouldn't be hard to enhance it with an image picker like you're doing in your module. It lets you define PHP code to fill PW select fields. Just install the module, then create a PHP file somewhere under site/templates that builds your selection: <?php namespace ProcessWire; # this is site/templates/selections/intranetforms.php $opts = []; foreach(scandir($config->paths->templates . "IntranetForms") as $d) { if(substr(strtolower($d), -4) != '.tpl') continue; $name = preg_replace('/\.tpl$/i', '', $d); $opts[] = array("label" => $name, "value" => $name); } return $opts; Then add a field of this type, enter the relative (to site/templates/) path to the PHP and make sure to check the filename option before saving.
  17. How about the matrix field type (probably depends on how many properties there are, since all will be presented on every page)?
  18. Hi Pascal, the question might be redundant, but have you ruled out network latency between the web and database server (e.g. by testing with a local copy)? Are you using InnoDB? If not, that might be a chance for a noticeable improvement. Otherwise, you're at a size where you can't follow a simple optimization guide, and your approach with lazy generation of cache parts is certainly going to be one piece of the puzzle. Our corporate intranet is a little under half of the size of your site given the figures above, but we have a lot less (speak roughly 1000) users. We're using a mix of relatively straight forward markup cache with invalidation on page save and regeneration on access every n minutes/hours, and a few cron jobs that assemble more complex dynamic content and stuff it into memcache, so our approach is not that much different from yours. It might make sense to hook into page save and store as much relevant page data as possible in a single (hidden) field in JSON and only load data that you have to get to through the page's accessors (multi language text fields, page references etc.) to reduce the number of queries, adding the delay to the save operation instead of every load. What @bernhard forgot to mention is that his RockFinder module might be worth a look in that regard.
  19. Both would probably be doable, though a massive use of data URIs would probably also necessitate caching them, or processing time and memory use may lead to performance issues. Here's a small example Textformatter module that converts image links in HTML fields to data URIs. You can set a size limit in the module configuration so any images equal or bigger in file size aren't converted. The default is really conservative (16kB) since not requesting larger images through regular client caching will do more for recurring visitors than saving them a roundtrip but squeezing the images over the wire with every page reload. <?php namespace ProcessWire; /** * Proof-of-concept module for the ProcessWire CMS * * Implements a textformatter for HTML fields that turns regular src URLs to * inline data URIs to avoid separate http requests. * * The default maximum size for images to inline is 16kB. * Feel free to raise this in the module's configuration. * * This code comes without restrictions or warranties. */ class TextformatterImgDataUri extends Textformatter implements Module, ConfigurableModule { protected static $defaultDataUriLimit = 8192; public static function getModuleInfo() { return array( "title" => "Textformatter Image Data URI", "summary" => "Replace image URIs in HTML body with their encoded data URI to speed up page loads", "version" => "0.0.6", ); } public function __construct() { $this->dataUriLimit = self::$defaultDataUriLimit; } public function format(&$str) { $src = preg_replace_callback('/(<img[^>]+(?<!-)src=)(["\'])(.*?)(\\2)([^>]*>)/', function($match) { // Only use files in site/assets/files if(strpos($match[3], $this->config->urls->assets) === 0) { // Translate url to paths $filepath = str_replace($this->config->urls->files, $this->config->paths->files, $match[3]); // If pagefileSecure is active, the path prefix probably needs to be inserted if(!file_exists($filepath) && $this->config->pagefileSecure && strpos($filepath, $this->config->paths->files . $this->config->pagefileSecurePathPrefix) !== 0) { $filepath = str_replace($this->config->paths->files, $this->config->paths->files . $this->config->pagefileSecurePathPrefix, $filepath); //$this->log->save('imgdatauri', sprintf(_("Added pagefileSecurePathPrefix to file path %s"), $filepath)); } // If the file isn't found, we return the original url if(! file_exists($filepath)) { //$this->log->save('imgdatauri', sprintf(_("File %s not found"), $filepath)); return $match[0]; } // Only output images inline that are smaller than the value set in the module's configuration $fsize = filesize($filepath); if($fsize >= $this->dataUriLimit) { //$this->log->save('imgdatauri', sprintf(_("File %s too big, %d >= %d"), $filepath, $fsize, $this->dataUriLimit)); return $match[0]; } // On windows, make sure extension php_fileinfo.dll is loaded $mime = mime_content_type($filepath); $dataUri = "data:$mime;base64," . base64_encode(file_get_contents($filepath)); return $match[1] . $match[2] . $dataUri . $match[4] . ' data-orig-src="' . $match[3] . '"' . $match[5]; } //$this->log->save('imgdatauri', sprintf(_('Not an img with src: %s'), $match[0])); return $match[0]; }, $str); $str = $src; } public function formatValue(Page $page, Field $field, &$value) { $this->format($value); } public function getModuleConfigInputfields() { $wrap = new InputfieldWrapper(); $f = $this->modules->get("InputfieldInteger"); $f->label = _("Maximum size to convert"); $f->attr('name', 'dataUriLimit'); $f->attr('value', $this->dataUriLimit); $f->description = _("Images smaller than the value entered here in bytes will included directly in the HTML (base64 encoded data uri) instead of their links. Note that this text formatter only affects images under site/assets/files"); $wrap->append($f); return $wrap; } }
  20. It is better to use $sanitizer->selectorValue() to get the value in the correct shape to be used in a selector than to add quotes yourself, though @Zeka's example works. $title="谢师宴 11am-1pm, Test服务奖 service award"; $eventspage = $pages->get(1085); //eventspage $selector = "title=" . $sanitizer->selectorValue($title); $findeventspage=$eventspage->events->findOne($selector); If a hand coded selector doesn't yield any results, it's always worth checking whether the PHP file is in the correct encoding (UTF-8). Access restrictions may also come into play, which can be easily checked by appending ", include=all" to the selector to ignore access checks.
  21. The Page List Select (InputfieldPageListSelect) is rather specific and can't deal with a flat list of pages, that's why the code option is disabled there. I don't think there's a way to extend it to do what you want. To implement something similar, you'll need to write your own Inputfield implementation (and perhaps even extend InputfieldPage itself), that's what I meant above with "serious coding". You'd need to write both the PHP module code and the JS that does AJAX pagination and tree expansion, so it's not something done in an hour.
  22. The tree selection is probably not possible without some serious coding. You can get a long way though by using what's already built into PW. I'd do it like this: Create your repeater field (lets call it "quotes") with the field for the quote text (I'll call it "quote") and add it to a template Create pages with that template and add a few quotes Add a field of type "Page Reference" (let's call it "quotelink"), set it to "Single Page or Null Page" On the Input tab for that field, select "Custom format (multiple fields) ..." in the "Label field" box, and in the "Custom page label format" input enter "{custlabel}" (we will fill that virtual property later) Expand the "Custom PHP Code" option in the "Selectable pages" section, copy the code there to site/ready.php and adapt it to walk through the persons and add all quote repeater items to the output. In that code, populate the "custlabel" property for each repeater item so the displayed text makes sense (e.g. by prefixing the first 80 characters of the quote with the person's name). Here's an example: <?php $wire->addHookAfter('InputfieldPage::getSelectablePages', function($event) { if($event->object->hasField == 'quotes') { $out = new PageArray(); $persons = $event->pages->find('template=person'); foreach($persons as $person) { foreach($person->quotes as $quoteitem) { $quoteitem->custlabel = $person->fullname . ": " . substr($quoteitem->quote, 0, 80); $out->add($quoteitem); } } $event->return = $out; } }); Add the quotelink field to a template Now you should be able to add a page with that template and select an arbitrary quote in your quotelink field In your template code, output $page->quotelink->quote
  23. How about renaming the old file before deleting it and adding the new one with the same name?
  24. Sadly, gitlab has known that the forward slash is problematic for quite a while now but the resolutions keeps being pushed from milestone to milestone. There are a lot of mail servers and webmail clients out there that can't deal with these slashes (MS Exchange and Debian's exim default configurations are such examples), so any solution likely involves moving to more common delimiters in the local part of such gitlab addresses, which in turn means that PHP's built-in email sanitizing filter (which $sanitizer->email uses) will then be sufficient. To speed things up, it might help to upvote the corresponding gitlab issue.
  25. The message complains about $u being empty (i.e. a NullPage object), so the call to $users->add didn't succeed. A likely cause is that a user with the name you are trying to add already exists. You should check the return of $users->add, or even better, check if the name in question is already there beforehand and output a message accordingly (and don't forget to sanitize the form input). There's also a nice API to access GET and POST parameters in ProcessWire. Instead of using e.g. $_POST["submit"], you can access $input->post->submit and don't trigger warnings/errors when that parameter isn't defined.
×
×
  • Create New...