Jump to content

FireWire

Members
  • Posts

    480
  • Joined

  • Last visited

  • Days Won

    31

Everything posted by FireWire

  1. Dangit. Sorry to hear that. The inline CKEditor thing is on my list but won't be added until the next version which I'm working on right now. The way that field renders is much different from other fields. I'll be sure to add that to the priority list to see what can be done.
  2. I haven't experienced that. I don't often use tables and can't remember if I've seen that. Fluency wouldn't be able to change how a field is rendered.
  3. Translating only for missing content should be good for performance. I do think that there are some items to consider. If this is used in a template when the page loads and caching is used then this function isn't guaranteed to execute since a pre-rendered HTML document would be returned to the browser. Caching is a good idea for performance so this would create a situation where you can't use caching and that would be a performance hit. If the content has been changed but you're only checking for the existence of translated text then it wouldn't re-translate. I have a solution for this but it would take a little extra code (detailed below) There is a possibility that using the function you wrote could mean additional calls to the database. Someone with a little more knowledge of the ProcessWire core could correct me if I'm wrong. If that's the case then there could be a performance hit that is dependent on how performant your database setup is. Performance difference would depend on how performant your DB and DB connection is. If you are running this loop on page load then the performance hit would really come from the delay in response from DeepL. Translation can take a couple of seconds in some cases and that will slow your page load time down a lot. This would only happen as long as something needs to be translated. If not then it will skip over the field and the page will load. As for the translation return value- the module returns a passthrough payload directly from the DeepL API (I didn't develop the return data structure). This is good because it is predictable and unchanged from DeepL documentation, and it makes sense when you consider the ability to translate multiple separate texts at once. Take some time to review the README.md file in the Fluency module directory, it has documentation of the return data structure and details on using the module directly. I'll make a note to let module users know that information is there for review. Solution for tracking changed content: One way to verify content is to use hashing and WireCache. I am developing the next version of Fluency that will have a solution for this but in the meantime here is a modified solution that may fit your use case: <?php /** * Analyzes the content in a field determines if it has changed since the last time it was checked * @param Page $thisPage Page containing field to check * @param string $fieldName Field object to check content for * @param string $lang Name of language to check content for default is PW's default language * @return bool|null Bool for content change, null if field doesn't exist on page */ function fieldChangedSinceCheck(Page $thisPage, string $fieldName, string $lang = 'default'): ?bool { if (!$thisPage->hasField($fieldName)) { return null; } $thisPage->of(false); $pageField = $thisPage->fields($fieldName); $isMultilanguageField = $pageField->type instanceof FieldtypeLanguageInterface; // Handle multi-language field if ($isMultilanguageField) { // Get the language, content in that language, and create a unique tracking ID $language = wire('languages')->get($lang); $current_field_content = $thisPage->$pageField->getLanguageValue($language); $key = "{$thisPage->id}|{$pageField->id}|{$language->id}"; } // Handle non multi-language data if (!$isMultilanguageField) { $current_field_content = $thisPage->$fieldName; $key = "{$thisPage->id}|{$pageField->id}"; } // Create a unique hash for the current content in this field in this language on this page $current_field_content_hash = hash_hmac('sha256', $current_field_content, $key); // Search WireCache for a previously stored content hash under this key if it exists, otherwise null $cached_field_content_hash = wire('cache')->getFor('field_content_tracking', $key); $contentHasChanged = false; // Compare the hash we created for the current content and compare it with the hash previously stored // If they do not match either it does not exist, or the content has been changed since it was last // analyzed if ($current_field_content_hash !== $cached_field_content_hash) { // Store the current content hash which will be used later to compare if content has changed wire('cache')->saveFor('field_content_tracking', $key, $current_field_content_hash); $contentHasChanged = true; } return $contentHasChanged; } There is a caveat, this solution will only tell you if it has updated since the last time it checked. So if a field was changed and you check the function will return true. If you check it again it will return false because it only tracks if it has changed since the last time it checked. I think this will still work for your use case as long as you act on it when it returns true. It works with multilanguage fields, regular fields, any language, and returns null if you try to check a value for a field that isn't on the page. Hope it helps!
  4. So I'm working on a new release and I just found this the other day myself. To fix this right away, change the following on line 172: <?php // From: wire('log')->save(self::ERROR_LOG, $message); // To: wire('log')->save(self::ERROR_LOG, $output['message']); This line means that there was a problem with DeepL. Either it had trouble connecting or DeepL returned an error when you tried to translate. In the ProcessWire admin take a look at the Fluency log, I think it may be named "deeplwire-api". It should contain the error to help you troubleshoot.
  5. PM me if I can share any info that would help get you up to speed. Can share some configs, code, or answer some questions if you need. I spent a lot (a lot) of time on tuning our setup and would be happy to share. Also, snapshot your "perfect setup" on DO before you host anything on it. I have one that is a template for our servers and I can spin one up in minutes. Also gives you some extra confidence with experimenting when you know you can nuke a server and start over with a machine built how you like it.
  6. I don't know why I wasn't thinking about NPM security issues... that was a dumb on my part haha.
  7. That's a pretty great strategy. I've thought about moving builds to the server, my approach will probably be updating the hook below to run a Gulp build script automatically. Question about your pre-push hook, does that make it possible to accidentally overwrite production code when the local branch is behind master? Asking since I haven't used a pre-push for deployment and I'm wondering if the files are being copied to the server before your local repo finds out that it could be behind the remote on Github. I'm going to describe our full setup for clarity because we don't use managed servers and that requires a bit more configuration. I included some details at the end to use this with managed hosting which is easier. On our servers there is a Linux user called 'deployment' which contains bare Git repositories for each site in '/home/deployment/sites' with this post-receive hook. #!/bin/bash while read oldrev newrev ref do if [[ $ref =~ .*/main$ ]]; then echo "Main ref received. Deploying to production..." sudo git --work-tree=/path/to/hosting/directory --git-dir=/path/to/deployment/repo checkout -f # This shouldn't be required on managed hosting setsid sudo chown -R www-data:www-data /path/to/hosting/directory > /dev/null 2>&1 < /dev/null & setsid find /path/to/hosting/directory -type d ! -perm 755 -exec sudo chmod 755 {} \; >/dev/null 2>&1 < /dev/null & setsid find /path/to/hosting/directory -type f ! -perm 644 -exec sudo chmod 644 {} \; >/dev/null 2>&1 < /dev/null & else echo "Ref $ref received. Not deploying production: only the main branch may be deployed on this server." fi done Locally in we have an additional remote called 'production'. We also use this for a deployment to staging where the remote only accepts pushes from the 'development' branch. So using 'git push' pushes to our Gitlab repo, and 'git push production' sends code live. production deployment@website.com:sites/website.com.deploy.git (push) Thinking about it now it would be a good idea to write a bash script that pushes to production only when the push to Gitlab is successful to further ensure all main branches match (writing myself a todo for this). Things I like about this approach: Only files that have changed are copied to the public directory which is fast and efficient PW core, modules, and extensive application code we have in /site outside of the templates directory are included. Things like PW logs and translation files are excluded via .gitignore. Config values are kept in a .env file so 'config.php' still lives in the repo and changes can be pushed. It is not possible for anyone to overwrite work that was pushed because the local branch will be behind the production branch. Server login passwords are disabled at the OS level so SSH keys are used. Pushes require no password. I wrote an interactive bash script on the server to add new sites which automatically creates hosting directories, Apache virtual host file, and deployment repository all from pre-written templates. Keeps the setup predictable, error free, easy to use consistently with very little work. When I complete the testing suite I'm going to add a pre-push hook locally and modify the post-receive hook to execute tests and require that all pass before deploying. Eventually I'll be putting all of this on a CI/CD pipeline but for now this smaller scale approach is just fine. I don't have the time to revamp our deployment strategy at the moment haha. Differences in hosting environments- For un-managed hosting the lines that begin with 'setsid' are required to change ownership to the Apache user and set file permissions in the hosting directory after copy. If you're managing the web server you probably already know what to do as far as user/permission management for 'deployment'. For managed hosting (I use Dreamhost for some projects) no user/permission configs are required so all the 'setsid' lines can be deleted. Only SSH access and Git on the managed hosting server are needed. Just create a sister directory to your website directory, initialize a bare repo with 'git init --bare', add the post-recieve hook with the proper directory locations, and remember to 'chmod +x' your post-receive hook file. This can probably be optimized more but I've been using it for years and it works ¯\_(ツ)_/¯
  8. I've been interested in sharing my setup since it's radically changed over the last year for the better. Wish I could open the repo for the code of my flagship project, but it's the site for the company I work for and isn't mine, www.renovaenergy.com Local Dev: Code editor is Sublime Text tuned for my preferences/workflow. OS is Ubuntu Linux, will probably distro-hop at some point like Linux users do. Environment is provided by Devilbox, which I can't recommend enough. It's a fast (mostly) pre-configured yet customizable Docker tool with outstanding documentation. A ProcessWire ready container is available. CSS/JS compiled by Gulp/Babel/Browserify for local dev and production builds. ES6 modules. Zero frameworks, no jQuery. Focus on lightweight JS and code splitting for better load times. CSS is compiled and split into separate files by media queries which are loaded by browsers on demand based on screen size. Currently building out website unit/integration tests using Codeception. This is becoming increasingly necessary as the site becomes more complex. Firefox Developer Edition Tilix terminal emulator, Quake mode is awesome Cacher stores code/scripts/configs in the cloud for easy sharing across machines. IDE integration is solid Meld for fast diffs WakaTime because who doesn't like programming metrics for yourself? DevDocs but locally in a Nativefier app. REQUEST: Star ProcessWire on Github. If a project has 7k+ stars it is a candidate to have it's documentation added to DevDocs. Production: Code editor is Vim on server Deployment is via Git. Local repositories have a secondary remote that pushes code to production via a bare GIT repo which updates assets on the server using hooks. Access to server via SSH only. Changes to files only made locally and pushed. Hosting by DigitalOcean with servers custom built from OS up for performance/security. Custom PageSpeed module implementation. Automatic image conversion to webp, file system asset caching, code inlining, delivery optimization, cache control, etc. Driven down TTFB <=500ms on most pages with load times around 2 seconds sometimes less if I'm lucky haha StatusCake monitors uptime, automated speed tests, server resources, and HTTPS cert checking. PagerDuty is integrated with StatusCake so issues like servers going down, server resources (ram/disk/memory) low, and whatever else get notifications on all your devices. 7G Firewall rules are added to the PW .htaccess file to block a ton of bots and malicious automated page visits. Highly recommended. Mailgun for transactional email ProcessWire Modules & Features: Modules (most used): CronjobDatabaseBackup, ProFields, Fluency, ImageBlurHash, MarkupSitemap, PageListShowPageId, ProDevTools, TracyDebugger, ListerPro, ProDrafts Template cache. We used ProCache initially but saw some redundancies/conflicts between it and PageSpeed tools on the server. Would absolutely recommend ProCache if your hosting environment isn't self-managed. All configurations are saved in .env files which are unique to local/staging/production environments with contents stored as secure notes in our password manager. This is achieved using the phpdotenv module loaded on boot in config.php where sensitive configurations and environment-dependent values are made securely available application-wide. Extensive use of ProcessWire image resizing and responsive srcset images in HTML for better performance across devices. URL Hooks - Use case- We rolled out a Web API so external platforms can make RESTful JSON requests to the site at dedicated endpoints. The syntax resembles application frameworks which made development really enjoyable and productive. The code is organized separately from the templates directory and allowed for clean separation of responsibilities without dummy pages or having to enable URL segments on the root page. Also allowed for easily building pure endpoints to receive form submissions. Page Classes - My usage -This was a gamechanger. Removing business logic from templates (only loops, variables, and if statements allowed) and using an OOP approach has been fantastic. Not sure if many people are using this but it's made the code much more DRY, predictable, and well organized. Implementing custom rendering methods in DefaultPage allowed for easily "componentizing" of common elements (video galleries, page previews, forms, etc) so that they are rendered from one source. Helped achieve no HTML in PHP and no PHP in HMTL (with the exceptions above). Also allows for using things like PHP Traits to share behavior between specific Page Classes. I completely fell in love all over again with PW over this and now I couldn't live without it. This literally restructured the entire site for the better. Probably other stuff but this post is too long anyway haha.
  9. That was something I considered after I posted that message. I use Docker for development and for some reason if I am connected to a VPN then DeepL fails to connect and it causes PW to not load admin pages. It is probably a networking configuration in the Docker image. This may be an issue that could exist with some dev environments, but that's just a semi-educated guess haha. Glad you are enjoying the module! Please let me know if you experience any issues. The next version is coming out soon and it will be going from alpha to beta version and have a bunch of new features. Any feedback is greatly appreciated!
  10. Next would be to check the ProcessWire logs under Setup > Logs > exceptions, and Setup > Logs > errors to see if there are any PHP errors since it looks like Fluency is able to connect to DeepL.
  11. Hey there WireDevelopers. I've updated this module with new features and stronger performance. If you are using the old version, an upgrade is strongly recommended. If not, see if it works for you.
  12. Hmmmm, interesting. Can you confirm the following: The "DeepL API Key" field has your API key entered and that it says "API key validated" below it? Under "DeepL API Account Usage For Current Billing Period" it shows values for Character Limit, Characters Translated, Characters Remaining, and Total Usage? (It is OK if Characters Translated is 0 and Total Usage is 0%) The Source/Destination Languages are taken from DeepL so this is the first step to make sure that module can connect to their service. I haven't tested this on the dev branch, but I don't believe there are any changes substantial enough to affect Fluency.
  13. That's a pretty rad idea as well. The API endpoints are all set virtually using URL hooks so the process comes up as ProcessPageView in that context.
  14. I think your solution works for a bigger context of where a lot of data on a page would be dependent on the context it was created in. I just needed to make sure that the hook for one field can determine at runtime whether the field was filled by a user in the PW admin or the Page API. Your solution from the PW admin side did make me think bigger picture which is also a possible solution for my application. This field is being used on pages that can be created/edited in the ProcessWire admin, but they can also be created/modified by a website REST API that other systems- in our case Salesforce- can use to create/modify data on the site. Calls to this website API are authenticated using ProcessWire users which have an API key assigned and a role of web-api-access. So rather than focus on verifying where the the page was created at the field level, I could check that the user creating the field is an API user at the page level. I got a little more into the weeds with that description, but your idea helped me think of a different approach.
  15. This would be a good idea if I knew the context ahead of time but the content on a field could be entered/edited from anywhere after added to a template.
  16. So there I was diving into a technical hole and the solution was so simple to begin with. Excellent.
  17. Hey all, I have pages that can be created two ways, in the PW admin and via an endpoint where JSON data is sent. I have a hook that checks field data on page save and errors if it is not correct. I would like this to create an error in the PW admin using $this->errors, but if the page is created via the Page API in the script that handles JSON requests I want to throw an exception I can catch and handle accordingly for the JSON response. This also allows for setting a user friendly error message for the PW admin, and machine friendly data for API use. Trying to figure out how to detect which context that the page is being created in. A script, or in the PW admin and act accordingly. Is this possible?
  18. Solved the issue. Found the following issue in a post from 2015 using some Google search that described a similar/same problem. I was able to solve the issue by logging in as a different user, checking the checkbox, and then saving the page. After that the superuser role was then allowed to set/save the checkbox. Odd issue. I created two fields, one text and one checkbox. I was able to set the value of the text as a superuser but not the checkbox. Solution is to log in as a different user with access to this field, set a value, then logout and then login as the superuser which will then allow the value to be saved.
  19. Like the title says. I've added a checkbox field to the User template, checking it and clicking save on the page doesn't store the value and the checkbox is always unchecked when the page loads. I'm creating a hook that works with this template and checkbox value for addHookBefore Pages::save. Accessing the value is always 0. Setting the checkbox value inside the hook doesn't save either. Inserting values into other fields in the same hook does save. I'm using $page->checkbox_field_name to access the value, and $page->checkbox_field_name = true when testing to try setting the value via API. More info: I'm editing this user profile under the Superuser role which shouldn't present any permission issues. I've enabled the checkbox under "What fields can a user edit in their own profile?", but shouldn't matter because I'm editing another user's profile. Checkbox value fails to save whether hook is present or not. PW v3.0.184 halp.
  20. Really appreciate your help as always! This was just a little bit of a deeper dive than usual and I was doing a lot of thinking out loud so to speak. Just putting the big questions out there. As for the `$http->error`, that property is only available after it tries two times.
  21. Got it. This introduces something that isn't noticeable even when reading the method documentation. In HTTP terms a 404 status isn't a failure, it's a successful response from the remote server that should be interpreted by the application making the request. When I read fallback, I assumed that it's because the request attempt failed, for example if CURL isn't available on the system it falls back to fopen. A use case I have is to check that a page exists and then let the user know, so a 404 is useful because the user is shown the error, they can fix the URL, and then the new URL is tested. Sending via 2 different methods the user must wait 3x longer. The immediate solution is to manually add parameters to every WireHttp call to disable the default configuration, but that seems like an extra step to keep WireHttp from ignoring a valid response. All that said, does this have some sort of benefit I'm not seeing? When should a 404 be treated like a failure rather than an informative response to a successful request? First time using WireHttp. I've always written my own CURL methods, just trying to make sure I'm not missing a benefit that this has.
  22. That's right, it's a 2 for 1 special. I am using WireHttp to make a GET request but it is actually making 2 requests when calling WireHttp::get() only once. It doesn't matter where I make this call, it always sends 2 requests. I stripped this down and wrote the request code in init.php to test with the bare minimum of ProcessWire booted. Here's my code: <?php namespace ProcessWire; /** * ProcessWire Bootstrap Initialization * ==================================== * This init.php file is called during ProcessWire bootstrap initialization process. * This occurs after all autoload modules have been initialized, but before the current page * has been determined. This is a good place to attach hooks. You may place whatever you'd * like in this file. For example: * * $wire->addHookAfter('Page::render', function($event) { * $event->return = str_replace("</body>", "<p>Hello World</p></body>", $event->return); * }); * */ if (!defined("PROCESSWIRE")) die; $http = new WireHttp; // Create unique URL with random number to track method call vs. requests made. $url = 'https://renovaenergy.ngrok.io/processwire/test/init/' . rand(1, 9999); $http->get($url); die; Here's the requests I'm receiving after loading the page once: That test was done with all modules removed from the modules directory and the module cache cleared. I can't think of what is causing one get() call to create two requests. Running PW v3.0.184
  23. @monollonom Good observations on that. I think the regex section could use an example like the others on that page. I just wasn't able to visualize that. Many many thanks for your assistance an insight on this!
  24. @dotnetic I picked it up after developing our API in Slim and looking at some best practices. It would be really great if this was the ProcessWire default.
  25. Some follow-up thoughts. Even though @monollonom had a more elegant Regex string that works, I still wanted to find out if I could make my ugly string work since technically it should have and there might be a case where an alternate isn't possible. I found some curiosities that may help some other people if they run into a similar problem. Original string: '/api/v1/offline-events/{eventId:#(?=.{15}$)([A-Za-z0-9])\w+#}/?' First off, I had to remove the # regex delimiters at the start/end. Then I had to replace the {eventId:regex} curly braces and apply parenthesis (eventId:regex) then it worked with the ugly regex. New String: '/api/v1/offline-events/(eventId:[[:alnum:]]{15})/?' This was a little confusing because in the URL hooks documentation I just noticed that /route/(variable) and /route/{variable} are used interchangeably. The second issue is that the documentation states the following: Using any of the !@#% characters at the beginning/end of the regex expression caused it to fail whether using {} or () to surround the route argument. So long story boring, I'm not sure if the documentation could be clarified or if there's a bug somewhere (or if I'm just reading it all wrong). Documentation I am referring to here for reference. Something @ryan should review?
×
×
  • Create New...