Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 07/24/2024 in all areas

  1. Pasting from office applications is a complicated topic. The Windows clipboard can offer different formats of its contents to applications you paste to, e.g. plain text, HTML or image data, or different other data formats both the application you copy from and the receiving application have registered with the Windows system. In the case of copying from Word, your clipboard will look similar to this: The paste handler in the receiving application needs to be able to parse one of the provided formats. You'd think there's HTML in that list, so TinyMCE could simply paste that and all should be well - well, there's a caveat. Office tries to be nifty and conserve memory, so the HTML in the clipboard contains links to local files, which look like this in the source: <img width=224 height=237 src="file:///C:/Users/Someone/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png" v:shapes="Grafik_x0020_1"> Into the game come security zones and same origin policy, and so no, file:///somewhere isn't going to make it into your inline editor. Safari on MacOS is a different kind of beast. The pasted HTML contains the images inlined as blob URIs, something similar but not completely interchangeable with data URIs. There's a plugin for TinyMCE that promises to be able to paste the full HTML with the images and even call an upload handler with the image data. No idea if that one parses the RTF version of the clipboard instead or does some other magic. But, OTOH, I really wouldn't want my users to paste formatted content from office apps. Been there, seen the disaster, and never want to go back there. It never plays well with surrounding HTML, it tends produce awful glitches when scaling things and in the end, the frustration on the users' side always far outweighs the initial convenience. In case you're curious about the things available in your Windows system's clipboard, you can take a peek with InsideClipboard (that's how I took the screenshot above).
    3 points
  2. Since it came up in this question and I had some time to kill in front of my computer waiting for updates to finish which I had to verify, I got curios whether copy & paste from Word on Windows into a TinyMCE editor field could be made to insert the formatted text and keep the images. Surprisingly, with the help of rtf.js, this went pretty quickly. Ryan already built automatic upload functionality (called ImgUpload) into the TinyMCE field, so I only had to enable the option and select a target field. Even though pasting word processor generated HTML is and always has been a sin, I built a small module for it anyway. I called it (I know, it sounds a bit clunky, but it was the best I could come up with, I'm a backend guy): RtfPasterTinyMce Usage Download the contents of this repository and unpack into a folder in site/modules Open ProcessWre admin and select Modules -> Refresh Click "Install" for "Rtf Paster TinyMCE" Go to "Fields" and select your TinyMCE field where you want to paste office content including images Check "rtfpaster" in "Additional plugins" on the "Input" tab and save your field configuration Edit a page with that field and copy a passage that contains both text and images from your word processor into your TinyMCE field. You should see your images there. Advanced Go into InputfieldTinyMCE's module settings and enable "Image fields for ImgUpload" Edit your TinyMCE field and select an existing image field in the "Image fiels for ImgUpload" select on the Input tab Paste some text / images mixture from your word processor Tadaa! Your images are magically uploaded into the selected field. Since the RTF doesn't contain any information about the file name of the source image, your uploaded images will be named fieldname.png, fieldname-1.png, fieldname-2.png etc. Keep in mind that this is so far a proof-of-concept module and hasn't been tested with different scenarios and source applications yet. Don't use it in production unless you feel confident to fix any errors yourself!
    2 points
  3. @Klenkes I currently have the same problem with v 1.0.9, after a downgrade to v 1.0.8 everything works as expected again.
    2 points
  4. The script is in my templates/init.php file. You have to change the switch statement. Change zh to pt and replace de with es. You need to put google on an exclusion list, so the bot get's not redirected! That is important, because the bot will visit with "en" language and will kick out all other language out of the index, if you force a redirect on them. The redirect function: function crawlerDetect($USER_AGENT){ # Bots we DON'T want to redirected $crawlers = array( 'Google' => 'Google', 'Lighthouse' => 'Chrome-Lighthouse', 'MSN' => 'msnbot', 'Rambler' => 'Rambler', 'Yahoo' => 'Yahoo', 'AbachoBOT' => 'AbachoBOT', 'accoona' => 'Accoona', 'AcoiRobot' => 'AcoiRobot', 'ASPSeek' => 'ASPSeek', 'CrocCrawler' => 'CrocCrawler', 'Dumbot' => 'Dumbot', 'FAST-WebCrawler' => 'FAST-WebCrawler', 'GeonaBot' => 'GeonaBot', 'Gigabot' => 'Gigabot', 'Lycos spider' => 'Lycos', 'MSRBOT' => 'MSRBOT', 'Altavista robot' => 'Scooter', 'AltaVista robot' => 'Altavista', 'ID-Search Bot' => 'IDBot', 'eStyle Bot' => 'eStyle', 'Scrubby robot' => 'Scrubby', 'Facebook' => 'facebookexternalhit', ); $crawlers_agents = implode('|',$crawlers); if (strpos($crawlers_agents, $USER_AGENT) === false) return false; else { return TRUE; } } # The setLang var is that for the hole session it will not enforce the redirect anymore # I have in the footer of my sites a language dropdown as override in case the user want to visit the # other language - or in case the automatic ist wrong $USER_AGENT = $_SERVER['HTTP_USER_AGENT']; if(crawlerDetect($USER_AGENT)) { $session->set('setLang','1'); }; if(!$session->get('setLang')) { if ($_SERVER['HTTP_ACCEPT_LANGUAGE'] != '') { # I take the first two letters of the sended language and compare it in a switch # statement $accept_lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'],0,2); switch ($accept_lang) { case 'de': $session->set('setLang','1'); if($page->url != "/") { $url = $page->localUrl('default'); $session->redirect($url); } break; case 'en': $session->set('setLang','1'); if($page->url != "/en/") { $url = $page->localUrl('en'); $session->redirect($url); } break; case 'zh': $session->set('setLang','1'); if($page->url != "/zh-cn/") { $url = $page->localUrl('zh-cn'); $session->redirect($url); } break; default: $session->set('setLang','1'); if($page->url != "/en/") { $url = $page->localUrl('en'); $session->redirect($url); } break; } } else { $session->set('setLang','1'); } }; The dropdown (is building also in the templates/init.php above the redirect function) - also make here the same changes as above, case 'default' to es (your base language set in ProcessWire and zh to pt, and the default case in the bottom to en, as the wish fallback: $current_lang = $languages->getLanguage(); switch ($current_lang->name) { # set all placeholder variable for the users language # also the attribute then for the html tag case 'default': $class_lang = 'language-de-de'; $hreflang = 'de'; $lang_url = $wire->page->localUrl("default"); break; case 'en': $class_lang = ''; $hreflang = 'en'; $lang_url = $wire->page->localUrl("en"); break; case 'zh-cn': $class_lang = ''; $hreflang = 'zh-cn'; $lang_url = $wire->page->localUrl("zh-cn"); break; default: $class_lang = 'language-de-de'; $hreflang = 'de'; $lang_url = $wire->page->localUrl("default"); break; } # The Dropwdon Linkt Text as PRocessWire Language Var, that can be set in the language module $drop_title = __("Sprache ändern"); $lang_li = ''; foreach($languages as $language) { // if(!$page->viewable($language)) continue; // is page viewable in this language? if($language->id == $user->language->id) { $lang_li .= "<li class='current'>"; } else { $lang_li .= "<li>"; $url = $wire->page->localUrl($language); $hreflang_drop = $home->getLanguageValue($language, 'name'); $lang_li .= "<a class='drop-item' hreflang='$hreflang_drop' href='$url'>$language->title</a></li>"; } } # Now comes the dropdonw. Build at with the framework/dropdown lib you want and change it accordingly. $drop_title for the titel of the menu # and then $lang_li for all the collect <li> above $lang_dropdown = ' <div class="dropdown"> <button class="drop-toggle" type="button" aria-expanded="false" onclick="dropFunc()"> <svg width="18px" height="18px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#fff" style="transform: translate(0, 3px);"><path d="M2 5H9M16 5H13.5M9 5L13.5 5M9 5V3M13.5 5C12.6795 7.73513 10.9612 10.3206 9 12.5929M4 17.5C5.58541 16.1411 7.376 14.4744 9 12.5929M9 12.5929C8 11.5 6.4 9.3 6 8.5M9 12.5929L12 15.5" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M13.5 21L14.6429 18M21.5 21L20.3571 18M14.6429 18L17.5 10.5L20.3571 18M14.6429 18H20.3571" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg> '. $drop_title .' </button> <ul id="drop-menu" class="drop-menu"> '. $lang_li .' </ul> </div> '; Then in my templates/_main.php in the footer, I call $lang_dropdown for the dropdown menu for manual override.
    2 points
  5. Why unfortunately? You are not blocking any requests this way and so you can just let the task run for as long as it needs. If you have a task which should run every 10 seconds but takes more than 10 seconds, you have a completely different type of problem 😊
    1 point
  6. I'm not 100% sure but I think I remember using this module for a similar task: https://processwire.com/modules/import-external-images/ Maybe try it first in a test installation.
    1 point
  7. Hey @FireWire Probably a great module...😉 I finally had the time to explore your module but I keep running into the same Deepl API errors and I don't know where to look for a solution or what to do next. This is PW 3.0.229 on PHP 8.2 Fluency 1.0.9 on a pretty basic PW testsite. Deepl API key shows as valid and active and in my Deepl account it says: Access to DeepL REST API When I click on translate the following happens: The error log shows: Engine: DeepLEngine /Error: BAD_REQUEST /Message: Invalid request: Expected 'text' parameter to be an array of strings /Response: {"message":"Invalid request: Expected 'text' parameter to be an array of strings"} The TRANSLATION modal shows only the word undefined in the target language. It's just a testsite, so no rush. I would be greatful for any advice.
    1 point
  8. With regards to SEO related features, I think that being natively built in to ProcessWire is not the best choice as it goes against the unopinionated nature of ProcessWire. If one were to develop a webapp where SEO is not necessary, those fields would cause bloat. But if this were to be an official Profield, it would be wonderful. Personally, I use the FieldsetGroup Pro field (I gave SEOMaestro a try but it didn't work the way I liked), but since that's now legacy, I will have to switch my approach at some point in the future. It looks like this (note: it used to have XML sitemap fields as well, however when WireSitemapXML came out, I removed them and let that module handle it): If there were to be an new SEO module similar to my screenshot (meaning it supports fields for X, Facebook and other social networks), I would hope it's not officially called "FieldtypeSEO" but rather something like "FieldtypePresence" (ChatGPT helped me with that one) since it's more fitting (it helps improve your site's "presence" via search engines and social media networks). Personally I dislike the word SEO as it represents a lot of what's wrong with the internet and the snake-oil behind it.
    1 point
  9. Please no 🙂 To elaborate, there are a couple of issues with this: Depending on how this is implemented, it could be a major turn-off for users like me, who have never (and likely will never) click that "download" link on the website, let alone use a configuration tool to get a download package. Most of the time I'm installing ProcessWire via Composer or by cloning it via Git, and obviously my wish would be that this process isn't made more complicated. On the other hand I've seen some users (beginners) kind of trip on things like "you'll have to install a module to get Repeaters working" or "you need to get a separate site profile". As such, I don't think this would necessarily be a good idea considering new users either. A configuration tool would likely be interesting mostly for advanced users, who know exactly what they want... and prefer to use a download link on a website to get it, which (I would assume) somewhat limits its usefulness. That being said, as long as I can still get a "viable" version of ProcessWire (most of what's currently in the core, and future additions as well, assuming that they are meaningful ones — which they undoubtedly will be!) without having to use a configuration tool, I of course don't mind if it is added as an additional option. If someone likes that kind of thing, then great, why not! Also if this would mean that non-essential core modules could be also installed one by one via Composer, that would be nice 🙂 What I'm missing here is the context: is this about getting a smaller downloadable package? Is that still a thing, e.g. is it really a problem for many users? Serious question, because I really don't know. I care a lot about performance on websites, but have never considered the download size of a CMS I'm using; unless it's in gigabyte range or something crazy like that. Or is it about dropping support for some core features? I'm not getting that vibe from here either, but that would make more sense to me, though in that case it might be better to just drop them from the core and perhaps find new maintainers for the packages if possible 🙂 Or is there some other benefit I'm missing?
    1 point
  10. @szabesz I think the phrase "everything necessary" might be worth considering here. A class ideally just has one responsibility. Page instances are there to provide an interface to one page's content and be swapped in and out of memory on demand. When there are things like hooks going into a Page, that changes the role completely. If you start doing this a lot on a large site then it may affect scalability. Maybe it's fine for one individual installation or another, however. In your case, this isn't an issue, as you've solved that concern with a static method. I think that's okay for an individual installation, but it wouldn't be okay for PW to suggest for everyone because it's not multi-instance safe. The core avoids also static methods when possible because they are disconnected from any instance of the class. When there are static methods, it's always worth looking closer if it's worth the convenience (sometimes it is) or if they would be better off somewhere else. Using hooks in your /site/init.php, /site/ready.php or /site/templates/admin.php (when appropriate) is a safe strategy, but I agree it's not great if you have a ton of them. At that point it might be good to have an autoload Module, or to start including your own files from one of the previously mentioned ones. But if you find it beneficial to use /site/classes/YourPageClass.php then also consider a separate class (ProductPageTools.php?). Rather than having a static method, you can have a static variable in your ProductPage class that refers to that instance, but there will only ever be one of them, and it will be multi instance safe. Page classes already do this for a lot of things, delegating to other classes (PageTraversal, PageValues, PageProperties, etc.) for code that can be shared among all Page classes. There's only ever one instance of those classes, no matter how many Page class instances there are. And they are all multi-instance safe. This also keeps the memory requirements of the Page classes very low, since the Page class has very little code itself, and mostly delegates to other classes where the same single instance operates on all Page classes. class ProductPage extends Page { static private $tools = null; public function wired() { parent::wired(); if(self::$tools === null) self::$tools = $this->wire(new ProductPageTools()); } } class ProductPageTools extends Wire { public function wired() { parent::wired(); $this->wire()->pages->addHook('saveReady(template=product)', $this, 'hookSaveReady'); } public function hookSaveReady(HookEvent $e) { // ... } } Maybe you have a method with a lot of code that you want to be callable from ProductPage (any number of instances) but maintain in ProductPageTools (only ever one instance). This is how the core delegates to other classes and limits the scale and scope of classes that may have many instances. class ProductPage extends Page { // ... public function foo() { return self::$tools->foo($this); } } class ProductPageTools extends Wire { // ... public function foo(Page $page) { $result = ''; // A whole bunch of code to generate $result from $page return $result; } } If the strategy you are using now works well for you then I'm not suggesting you change it. But just wanted to point out the strategy I'd suggest as a scalable and multi-instance safe one that also maintains separation of concerns and keeps the Page class from becoming complex or heavy.
    1 point
  11. You can have a look at https://github.com/baumrock/site-rockfrontend
    1 point
  12. You'd need to add name to your checkboxes like (example only with offers): <input name="offers[]" value="<?=$m->name?>" class="w3-checkreq" type="checkbox" id="a<?php echo $a;?>" /> Then on form processing you'd need to do sth like this: // sanitize array input $offers = $input->post->array('offers', 'pageName') foreach($offers as $offer){ // do your saving or whatever; }
    1 point
  13. The PW forum is a place of the most friendly and helpful people I ever interacted with in web based CMS/CMF forums so far. What I learned from other forums is, that showing as much code you already tried (e.g. I put this code xxx into /site/templates/myfile.php) and asking a single question closely related to your code example (e.g. I expected to get x, but instead I see y) would help all involved people to better grasp each others standpoint and to provide better replies to specific questions. Cheers cwsoft
    1 point
  14. I've worked around this by running LoginRegister's processes on another template, so all good for me now ??
    1 point
  15. This can now be done with owner selectors (http://processwire.com/blog/posts/processwire-3.0.95-core-updates/) $tagsThatHaveBeenUsedOnPosts = $pages->find('template=tag, tags.owner.template=post'); Where tags is the name of the field on the post template that holds the tag pages. Also, for any given tag page you can check how many post pages reference it with $page->references('template=post')->count(); https://processwire.com/blog/posts/processwire-3.0.107-core-updates/#what-pages-point-to-this-one
    1 point
×
×
  • Create New...