Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 08/30/2023 in all areas

  1. We are live and have a very special guest in the video ???
    3 points
  2. The client basically has two sites. One is in the UK and the other in the EU but serves customers internationally. There is a lot of shared information on both sites but at the same time they are different enough to warrant individual sites. But the client didn't want to, for example, add (and keep up-to-date) the same product twice. What I have set up is a root site that doesn't have a front-end but stores all the shared data. Then there is a folder for each site within that, each a separate PW instance. /root/ /root/uk/ /root/eu/ My question above was really to see if I could copy info between the sites within the same script. While Robin S's link was useful, it would appear to be for read-only so I didn't end up using it. Instead, the root site has hooks that saves all data into JSON files and associated assets and the EK and EU then take the and convert it to PW pages. If you're wondering why the EU and UK sites are within the root site folder as opposed to being siblings, it's because they share the same templates, models, JS, SCSS, etc.
    3 points
  3. Version 1.0.0 of the MarkupMenu module released: https://github.com/teppokoivula/MarkupMenu/releases/tag/1.0.0. This release includes a relatively minor, but also potentially breaking change: new argument $root was added to the MarkupMenu::renderArrayItem() method, between some of the existing params. Since this method is hookable this could be a problem for existing code, so I figured it was best to increment major version.
    2 points
  4. Interesting but I find it hard to digest this chart as being a useful overview. I keep looking at it and fail to "keep anything of it in my brain". I find the following "cheat sheet" much more informative and therefore usable: https://addyosmani.com/blog/script-priorities/
    2 points
  5. As a longtime photo enthousiastic i decided to show my work to the "world" What else could i do than use processwire? My Dutch site is viewable at: https://foto.markveldman.com Mark Veldman
    1 point
  6. If I had sites A and B is there a way in site A to create an instance of ProcessWire\wire() that is connected to a completely different site? The reason I ask is to sync some data between two different sites on the same server on the fly. E.g. on site A: $page1 = wire('pages')->get(/foo-bar/); $page1->of(false); $page2 = new \ProecssWire2\Page; $page2->template = $page1->title; $page2->title = $page1->title; // and so on $page2->save(); // Saves to site B
    1 point
  7. JavaScript Tag Loading Visualized by Wes Bos See here for more/discussion:
    1 point
  8. If you want to modify the Module you could replace the original switch case in FieldtypeTable.module with this: case 'text': $maxLength = empty($col['settings']['maxLength']) ? 2048 : (int) $col['settings']['maxLength']; $stripTags = empty($col['settings']['stripTags']) ? true : ($col['settings']['stripTags'] === 'false' ? false : true); $v = strlen("$v") ? $sanitizer->text($v, array('maxLength' => $maxLength, 'stripTags' => $stripTags)) : null; break; That will allow you to specify stripTags=false on text colums.
    1 point
  9. Thanks for confirming. Yes, I am currently using a Textarea rows="1" and removing line returns on save so it effectively does the same thing.
    1 point
  10. Unfortunately I believe this is hardcoded behaviour (see _sanitizeValue() in FieldtypeTable.module). As you noticed, it doesn’t strip tags from textareas, so maybe switch to that? It should be quick work to add this as a per-column option like maxLength. Perhaps worth suggesting it to Ryan in the ProFields forum?
    1 point
  11. I lived in Nederland many years ago and your portfolio made me home sick. :) Clean site and fantastic photography!
    1 point
  12. I have also done an multi-instance app. The context is a holiday cottage booking system where the admin system is one site and the public website is separate. The connection is for the availability and prices. There can be more than one public website, for multiple properties. That way, the (fairly simple) public site does not get entangled in the rather more complex admin app. If there was a simple way of segregating different categories of templates, fields and pages in the back-end then maybe it would not be necessary (I.e. different “views” of the database), but even so I think a physical separation is best.
    1 point
  13. I completely missed your thread on this when searching this issue - some helpful stuff there thanks.
    1 point
  14. He has different formats, but yes... "reaction" videos are special. Still I really enjoyed this one (and the original video).
    1 point
  15. I stumbled on this video by Primeagen. If you're not familiar, he's a highly experience polyglot developer that uses a lot of different languages regularly and talks about them on his channel. His content isn't really my style (I think this is one of the first "reaction" videos I've ever watched), but I enjoyed seeing him get reintroduced to PHP, especially since he states that he hasn't used it since PHP 5.x. The video he reviews is also a really great introductions to modern features of PHP 8+ that I think more developers should adopt- both because they are totally cool and really do provide a better developer experience. So come for the "PHP is cool again", stay for the syntax we should all be using. The original video was by the same person that @wbmnfktr shared in this comment earlier in the thread, if you haven't watched his other videos, check them out!
    1 point
  16. After 8 months in development we are excited to bring you ProcessWire 3.0.226 main/master. This version has a ton of great new features, improvements and optimizations, plus more than 100 issue fixes. This post takes an in-depth look at highlights from this great new version. While there's even more in this version than is covered fully here, we hope this gives you a good taste of what you'll find in 3.0.226! https://processwire.com/blog/posts/pw-3.0.226/
    1 point
  17. It seems you have more and more options nowadays to use everyday things. One thing I don't like about using the "defer" method (even it seems as the go-to method today) is that when you make use of frontend frameworks that manipulate the look of elements on your website this results in some ugly "jumping around of elements that are not aligned/styled yet because the script is waiting to be executed". I am using UIkit and when using "defer" on the uikit.min.js file it will execute after the HTML is parsed. So for a few milliseconds on page load you can see some of the dropdown menus visible in the main navigation for example. That is because I make use of the uk-navbar component, which relies on the uikit.min.js file: uk-navbar="align: center; animation: uk-animation-slide-top-small; animate-out: true" When loading the script the normal way (no defer or async) you see no dropdown menu at all when the page is loaded. Because then the script is executed before all of the page is loaded. I think even when there are best-practices how to to stuff you always should check what is more important for your project. Having a consistent look or save a few fractions of some milliseconds on loading time.
    1 point
  18. Oh wow, thanks. That looks (as everything tends to be with PW) so simple!
    1 point
  19. If your two sites are on the same server then this blog post explains how to do it: https://processwire.com/blog/posts/multi-instance-pw3/
    1 point
  20. Hello, I would really appreciate your comments on this blog post I am preparing for our site. =========== Creating a dynamic search with very little code in ProcessWire is easy. This search cannot compete with engines like Elasticsearch, Solr, and others. However, it is suitable for most "showcase" sites. Here is how we did it on the Spiria site using the small library htmx and its companion hyperscript. The goal The recipe Inclusion of libraries htmx and hyperscript (the latter is optional). A field of type textarea integrated to the page models that we want to index. A code for indexing the existing content in the file `ready.php` A search controller which we name here `api.php`. This controller will also be a page with the `api` template. A form placed in the pages requiring the search. Content indexing Before we can program, we need to index the content on which we want to apply our search. In my proof of concept, I have developed two strategies. This is probably overrated, because I am not sure of the gain in speed. Index for a single term search Indexing for a multiple term search To do this, we need to introduce two fields in each model where we want an indexation. The `search_text` field which will contain only one occurrence of each word on a page. The `search_text_long` field which will preserve all sentences without HTML tags. We place a hook in the `ready.php` page in this way: <?php namespace ProcessWire; pages()->addHookAfter("saveReady", function (HookEvent $event) { $p = $event->arguments[0]; switch ($p->template->name) { case "blog_article": $french = languages()->get('fr'); $english = languages()->get('default'); $txt_en = $p->page_content->getLanguageValue($english) . ' ' . $p->blog_summary->getLanguageValue($english); $txt_fr = $p->page_content->getLanguageValue($french) . ' ' . $p->blog_summary->getLanguageValue($french); $title_en = $p->title->getLanguageValue($english); $title_fr = $p->title->getLanguageValue($french); $resultEn = stripText($txt_en, $title_en); $resultFr = stripText($txt_fr, $title_fr); $p->setLanguageValue($english, "search_text", $resultEn[0]); $p->setLanguageValue($english, "search_text_long", $resultEn[1]); $p->setLanguageValue($french, "search_text", $resultFr[0]); $p->setLanguageValue($french, "search_text_long", $resultFr[1]); break; } }); And function stripText($t, $s) { $resultText = []; $t = strip_tags($t); $t .= " " . $s; $t = str_replace("\n", " ", $t); $t = str_replace(",", "", $t); $t = str_replace("“", "", $t); $t = str_replace("”", "", $t); $t = str_replace("'", "", $t); $t = str_replace("?", "", $t); $t = str_replace("!", "", $t); $t = str_replace(":", "", $t); $t = str_replace("«", "", $t); $t = str_replace("»", "", $t); $t = str_replace(",", "", $t); $t = str_replace(".", "", $t); $t = str_replace("l’", "", $t); $t = str_replace("d’", "", $t); $t = str_replace("&nbsp;", "", $t); $t = preg_replace('/\[\[.*\]\]/', '', $t); //$t = preg_replace('/\?|\[\[.*\]\]|“|”|«|»|\.|!|\&nbsp;|l’|d’|s’/','',$t); $arrayText = explode(" ", $t); foreach ($arrayText as $item) { if (strlen(trim($item)) > 3 && !in_array($item, $resultText)) { $resultText[] = $item; } } return [implode(" ", $resultText), $t]; } If you have the ListerPro module, it becomes easy to save all the pages to be indexed in batch and any new page will then be indexed as it is created. The `stripText()` function aims at cleaning up the text as we want. Note that, in my example, I distinguish between French and English. This little algorithm is entirely perfectible! I have commented a shorter way to clean up the text, but at the expense of comprehension. As mentioned before, it is probably unnecessary to create two search fields. The most important thing would be to optimize the text as much as possible, as many small words are useless. The current code restricts to words longer than three characters, which is tricky in a computing context like our site where names like `C#`, `C++`, `PHP` compete with `the`, `for`, `not`, etc. That said, perhaps this optimization is superfluous in the context of a simple content and not and limited in number. So now see the process and the research code. The structure structure.svg This scheme is classical and needs few explanations. The `htmx` library allows a simple Ajax call. The form code01.svg The form has a `get` method that returns to a conventional search page when the user presses the `enter` key. A hidden field with the secret key generated on the fly enhances security. The third field is the `input` of the dynamic search. It has a `htmx` syntax. The first command, `hx-post`, indicates the method of sending the data to the API. Here, it is a `post`. `htmx` allows to handle events on any DOM element. So we could have several calls on different elements of a form. The second line indicates where the API response will be sent when received, which is `div#searchResult` below the form. The `hx-trigger` command describes the context of sending to the API. Here, when the user releases a key, with a delay of 200 ms between each reading of the event. The `hx-indicator` command is optional. It tells the user that something is happening. In the example, the `#indexsearch` image is displayed. This is automatically handled by htmx. We have the possibility to pass other parameters to the search with the `hx-vals` command. The given example is simplified. We send the search language. The last command comes from the `hyperscript` syntax. It indicates that if you click anywhere outside the search field, you will make the contents of `#found` disappear, which will be described below. It is clear from this example that no javascript is called, except the [htmx] and [hyperscript] libraries. It is worth visiting the web site of these two libraries to understand their philosophy and possibilities. The Search API The API resides in a normal ProcessWire page. Although it is published, it remains "hidden" from CMS searches. This page allows requests to be answered and the correct functions to be called. Several requests to the CMS can be gathered in this type of page. <?php namespace ProcessWire; $secretsearch = session()->get('secretToken'); $request = input()->post(); $lang = sanitizer()->text($request["lang"]); if (isset($request['CSRFTokenBlog'])) { if (hash_equals($secretsearch, $request['CSRFTokenBlog'])) { if (!empty($request["search"])) { echo page()->querySite(sanitizer()->text($request["search"]),$lang); } } else { echo __("A problem occurred. We are sorry of the inconvenience."); } } exit; In this case : We extract the secret token of the session, token that will have been created in the search form page. We then process everything that is in the `post` query. Note that this is a simplified example. We compare the token with the one received in the query. If all goes well, we call the SQL query. Our example uses a class residing in `site/classes/ApiPage.php`; it can therefore be called directly by `page()`. Any other strategy is valid. The following code represents the core of the process. <?php namespace ProcessWire; public function querySite($q, $l) { $this->search = ""; $this->lang = $l == 'en' ? 'default' : 'fr'; user()->setLanguage($this->lang); $whatQuery = explode(" ", $q); $this->count = count($whatQuery); if ($this->count > 1) { $this->search = 'template=blog_article,has_parent!=1099,search_text_long~|*= "' . $q . '",sort=-created'; } elseif (strlen($q) > 1) { $this->search = 'template=blog_article,has_parent!=1099,search_text*=' . $q . ',sort=-created'; } if ($this->search !== "") { $this->result = pages()->find($this->search); return $this->formatResult(); } return ""; } protected function formatResult() { $html = '<ul id="found">'; if (count($this->result) > 0) { foreach ($this->result as $result) { $html .= '<li><a href="' . $result->url . '">' . $result->title . '</a></li>'; } } else { $html .= __('Nothing found'); } $html .= '</ul></div>'; return $html; } The `formatResult()` function is simple to understand and it is here that we see the `ul#found` tag appear which, remember, is deleted by the _hyperscript_ line of the form. _="on click from elsewhere remove #found" Actually, I've commented out this line in the production code so far. Instead of clicking anywhere, it would be better to add a background behind the form when doing a search in order to focus the `click` event. But again, all strategies are good! In the current code, it is not necessary to add CSS to display the result. It is placed in an empty `#searchResult` tag, so it is invisible at first. As soon as it is filled by the search result, everything becomes accessible, the CSS being targeted on the `ul#found` list and not on its parent. Conclusion The purpose of this article was to demonstrate htmx and the possibilities that this library allows. There is already an excellent search module for ProcessWire, SearchEngine, which can coexist very well with the code described here.
    1 point
  21. Very nice! In the context of this post in the PW forums I find myself looking for a link to your site so I can see the search in action. Maybe you could add a link? A tip: str_replace() accepts an array for the $search argument, so your stripText() function could be made more compact where it is removing multiple different characters.
    1 point
  22. For a basic approach: $breadcrumbs = $page->title; foreach($page->parents() as $parent) { $breadcrumbs = "<a href='{$parent->url}'>{$parent->title}</a> / " . $breadcrumbs; }
    1 point
×
×
  • Create New...