-
Posts
534 -
Joined
-
Last visited
-
Days Won
11
Everything posted by Sergio
-
Sendy is great if your budget is small AND you need to send emails to a lot of people (10k+). Mailchimp is incredible, but the price for small business is not so good, especially when a dollar is almost 4 times your currency (Brazilian Real) ? Just keep an eye on your AWS SES reputation dashboard, for complain and bounce rates.
-
I added the source code on this post:
-
The module source is below. Example usage: a checkbox on a contact form (using Form Builder) for the user to subscribe. It's used on https://ricardo-vargas.com/contact/ EXAMPLE A method on _hooks.php. If you don't use Form Builder, use this code on your form page. $forms->addHookBefore('FormBuilderProcessor::emailForm', function($event) { $processor = $event->object; if ($processor->formName == 'contact-form') { $formData = $event->arguments(1); $contact_name = $event->sanitizer->text($formData['contact_name']); $contact_name = substr($contact_name, 0, 30); // limit length further $contact_name = $event->sanitizer->emailHeader($contact_name); $contact_email = $event->sanitizer->text($formData['contact_email']); $contact_email = $event->sanitizer->emailHeader($contact_email); $processor->emailFrom = $contact_email; //reply to $processor->emailSubject = 'Message from '.$contact_name; $form = $event->object->getInputfieldsForm(); $subscribe = $form->get('receive_updates'); $list_id = $form->get('sendy_list_id')->attr('value'); // check to see if they subscribed if ($subscribe->attr('checked')) { $success_url = '/contact'; // $fail_url = '/contact?error=1'; $ProcessSendyAPI = wire('modules')->getModule('ProcessSendyAPI'); $ProcessSendyAPI->subscribeInSendy($contact_name, $contact_email, $list_id, $success_url); } } }); MODULE https://gist.github.com/sjardim/2d834ebb0bd66d4da1ac16072f4075cd <?php namespace ProcessWire; class ProcessSendyAPI extends WireData implements Module, ConfigurableModule { public static function getModuleInfo() { return array( 'title' => __('Process Sendy API'), 'summary' => __('Handle API calls to a Sendy installation'), 'author' => 'Sérgio Jardim', 'version' => '001', 'singular' => true, 'autoload' => false, 'icon' => 'envelope' ); } /** * Data as used by the get/set functions * */ protected $data = array(); /** * Default configuration for module * */ static public function getDefaultData() { return array( "sendy_api_key" => '', "sendy_installation_url" => 'http://www.example.com/sendy' ); } /** * Populate the default config data * */ public function __construct() { foreach(self::getDefaultData() as $key => $value) { $this->$key = $value; } } public static function getModuleConfigInputfields(array $data) { $data = array_merge(self::getDefaultData(), $data); $wrapper = new InputfieldWrapper(); $f = wire('modules')->get('InputfieldText'); $f->attr('name', 'sendy_api_key'); $f->label = __('Sendy API Key', __FILE__); $f->description = __('Further instructions at https://sendy.co/api', __FILE__); $f->notes = __('Get your key at http://your_sendy_installation/settings.', __FILE__); $f->value = $data['sendy_api_key']; $wrapper->add($f); $f = wire('modules')->get('InputfieldURL'); $f->attr('name', 'sendy_installation_url'); $f->label = __('Sendy instalation URL', __FILE__); $f->description = __('Your Sendy installation URL without a trailing slash', __FILE__); $f->notes = 'http://www.example.com/sendy'; $f->value = $data['sendy_installation_url']; $wrapper->add($f); return $wrapper; } /** * [subscribeUserOrGuest description] * @param [type] $name [description] * @param [type] $email [description] * @param [type] $list_id [description] * @param [type] $success_url [description] * @param [type] $fail_url [description] * @return [type] [description] */ public function subscribeInSendy($name, $email, $list_id, $success_url = null, $fail_url = null) { $api_key = $this->data['sendy_api_key']; $sendy_url = $this->data['sendy_installation_url']; $postdata = http_build_query( array( 'name' => $name, 'email' => $email, 'list' => $list_id, 'boolean' => 'true' //set this to "true" so that you'll get a plain text response ) ); $opts = array('http' => array('method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => $postdata)); $context = stream_context_create($opts); $result = file_get_contents($sendy_url.'/subscribe', false, $context); //check result and redirect if($result) { $this->wire('log')->save("newsletter", 'A new user subscribed to the site mailing list: '.$email); if($success_url) { header("Location: $success_url"); } } else { $this->wire('log')->save("error", 'Error occurred on subscribing '.$email); if($fail_url) { header("Location: $fail_url"); } } } }
-
Hi @php ! I'd like to recommend to you a free PHP course by Jeffrey Way, the guy behind Laracasts: https://laracasts.com/series/php-for-beginners I think you're going to love it.
-
I think you can take a look at the CSS "object-fit" rule. See this codepen: https://codepen.io/akb20/pen/EPoPpx
-
Thank you, Ryan! Keep the excellent work up! Cheers!
-
Forgot to say, welcome to the forums! ProCache is awesome. And if you were you, I'd take a look at @tpr's Latte module too. It's great!
-
Yes. Unless I'm utterly wrong. Twig will render PHP files at runtime, and their output in the browser is the same output that's processed by ProCache. ProCache will render static version of the pages in /assets/ProCache-xxxxx/page-name/index.html that will be server by .htaccess rules (or Nginx rules, like I do)
-
Yes, it will work. I use Latte template language, but it's pretty similar to Twig, and ProCache work flawlessly.
-
Hi @Lemoratoire, welcome to the forums. You need to change the field settings. So, go to /setup/field/ find the Body field and on the Input tab change you can add some tags to the "Extra allowed content'" field or, for a full control of what HTML tags you can use, you'll need to disable the "ACF" and the "Use HTML Purifier" options. But beware that this opens a security risk if someone else is entering code beside yourself.
-
@noelboss, this is BEAUTIFUL!! Very easy to understand. I'm eager to play with it and create a front-end using Vuejs and another with Gatsbyjs. Many thanks for this module!!
-
All of a sudden, with nothing changed on the database or server, a website was getting error when doing a search: Error: Exception: SQLSTATE[HY000]: General error: 23 Out of resources when opening file './your-database-name/pages_parents.MYD' (Errcode: 24 - Too many open files) (in /home/forge/example.com/public/wire/core/PageFinder.php line 413) #0 /home/forge/example.com/public/wire/core/Wire.php(386): ProcessWire\PageFinder->___find(Object(ProcessWire\Selectors), Array) #1 /home/forge/example.com/public/wire/core/WireHooks.php(723): ProcessWire\Wire->_callMethod('___find', Array) #2 /home/forge/example.com/public/wire/core/Wire.php(442): ProcessWire\WireHooks->runHooks(Object(ProcessWire\PageFinder), 'find', Array) #3 /home/forge/example.com/public/wire/core/PagesLoader.php(248): ProcessWire\Wire->__call('find', Array) #4 /home/forge/example.com/public/wire/core/Pages.php(232): ProcessWire\PagesLoader->find('title~=EAP, lim...', Array) #5 /home/forge/example.com/public/wire/core/Wire.php(383): ProcessWire\Pages->___find('title~=EAP, lim...') #6 /home/forge/example.com/public/wire This error message was shown because: you are logged in as a Superuser. Error has been logged. I tried several things, listed in this thread: https://serverfault.com/questions/791729/ubuntu-16-04-server-mysql-open-file-limit-wont-go-higher-than-65536 But for some reason, MySQL was not getting its limit increased, but in the end, the one that did the trick was this: This worked for me on Ubuntu Xenial 16.04: Create the dir /etc/systemd/system/mysql.service.d Put in /etc/systemd/system/mysql.service.d/override.conf: [Service] LimitNOFILE=1024000 Now execute systemctl daemon-reload systemctl restart mysql.service Yes indeed, LimitNOFILE=infinity actually seems to set it to 65536. You can validate the above after starting MySQL by doing: cat /proc/$(pgrep mysql)/limits | grep files
-
Really impressive, Diogo! Excellent implementation of the design. And cool stuff having the animations settings manageable by the design team. Congratulations to you and the team!!
-
You can also add class names on the ProcessPageEditImageSelect module options, but unfortunately UiKit lightbox plugin (https://getuikit.com/docs/lightbox) needs a wrapper div , so the code should be something like: <div uk-lightbox> <a href="image.jpg" data-caption="Caption"></a> </div> @Hurme, maybe you can try creating a new module based on@Martijn Geerts's Image Interceptor for this.
-
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com; root /home/forge/example.com/public; index index.html index.htm index.php; charset utf-8; # ----------------------------------------------------------------------------------------------- # Access Restrictions: Protect ProcessWire system files # ----------------------------------------------------------------------------------------------- # Block access to ProcessWire system files location ~ \.(inc|info|module|sh|sql)$ { deny all; } # Block access to any file or directory that begins with a period location ~ /\. { deny all; } # Block access to protected assets directories location ~ ^/(site|site-[^/]+)/assets/(cache|logs|backups|sessions|config|install|tmp)($|/.*$) { deny all; } # Block acceess to the /site/install/ directory location ~ ^/(site|site-[^/]+)/install($|/.*$) { deny all; } # Block dirs in /site/assets/ dirs that start with a hyphen location ~ ^/(site|site-[^/]+)/assets.*/-.+/.* { deny all; } # Block access to /wire/config.php, /site/config.php, /site/config-dev.php, and /wire/index.config.php location ~ ^/(wire|site|site-[^/]+)/(config|index\.config|config-dev)\.php$ { deny all; } # Block access to any PHP-based files in /templates-admin/ location ~ ^/(wire|site|site-[^/]+)/templates-admin($|/|/.*\.(php|html?|tpl|inc))$ { deny all; } # Block access to any PHP or markup files in /site/templates/ location ~ ^/(site|site-[^/]+)/templates($|/|/.*\.(php|html?|tpl|inc))$ { deny all; } # Block access to any PHP files in /site/assets/ location ~ ^/(site|site-[^/]+)/assets($|/|/.*\.php)$ { deny all; } # Block access to any PHP files in core or core module directories location ~ ^/wire/(core|modules)/.*\.(php|inc|tpl|module)$ { deny all; } # Block access to any PHP files in /site/modules/ location ~ ^/(site|site-[^/]+)/modules/.*\.(php|inc|tpl|module)$ { deny all; } # Block access to any software identifying txt files location ~ ^/(COPYRIGHT|INSTALL|README|htaccess)\.(txt|md)$ { deny all; } # Block all http access to the default/uninstalled site-default directory location ~ ^/site-default/ { deny all; } #Amplify dashboard location /nginx_status { stub_status on; allow 127.0.0.1; deny all; } # ----------------------------------------------------------------------------------------------- # If the request is for a static file, then set expires header and disable logging. # Give control to ProcessWire if the requested file or directory is non-existing. # ----------------------------------------------------------------------------------------------- location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|eot|woff|ttf)$ { expires 15d; log_not_found off; access_log off; try_files $uri $uri/ /index.php?it=$uri&$query_string; } # ----------------------------------------------------------------------------------------------- # ProCache Rules # ----------------------------------------------------------------------------------------------- set $cache_uri $request_uri; if ($request_method = POST) { set $cache_uri 'nocache'; } if ($http_cookie ~* "wires_challenge") { set $cache_uri 'nocache'; } if ($http_cookie ~* "persist") { set $cache_uri 'nocache'; } # ----------------------------------------------------------------------------------------------- # This location processes all other requests. If the request is for a file or directory that # physically exists on the server, then load the file. Else give control to ProcessWire. # ----------------------------------------------------------------------------------------------- location / { expires -1; try_files /site/assets/ProCache-b3d534d...d/$cache_uri/index.html $uri $uri/ /index.php?it=$uri&$args; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } access_log off; error_log /var/log/nginx/example.com-error.log error; error_page 404 /index.php; location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; fastcgi_index index.php; include fastcgi_params; } location ~ /\.ht { deny all; } }
- 56 replies
-
- 11
-
-
I never used this approach for more than one template, but you can also use the $expire parameter: $all_matches = $cache->save('all_articles', $pages->find("template=where-to-go-detail|our-guides-detail, sort=sort"), $expire = 432000); //week
-
What's the TTFB of a procached version of a page X (a complex one)? I think template cache will not get you any advantage over ProCache, but MarkupCache will as you can cache parts of the page before ProCache kicks in! Use markup cache for blocks of code that do complex queries or when querying multiple pages. Basic example: A website has hundreds of podcast episodes. You can cache a count of these episodes so you don't need to query it when the page cache is cleared in ProCache. $template = $templates->get("podcasts"); //episodes $all_episodes_count = $cache->get("all_episodes_count", $template, function($page) { return $page->NumChildren(true); }); $view->total_episodes = $all_episodes_count; The "all_episodes_count" cache file will only be cleared when a new page is created with the template "podcasts".
-
If you have ImageMagick installed on the server, you can use/adapt this little module by @Robin S: Examples of what's possible to implement yourself: https://stackoverflow.com/questions/32873690/color-overlay-with-opacity-using-imagemagick-in-rails/#answer-33100788
-
Like you said, $page->inscr_nr is a text field, not a repeater, so output its value directly, removing "->title": $content .= $page->inscr_nr . '</p></td></tr>';
-
Now that I've looked at the Network inspector, I know why: it took 22 seconds to load all the 25.4MB of images here in Brazil in a 60 Mb/s connection.
-
Very nice!! Loved your work, specially Fabricius logo and website. Great job!! The only criticism I can make is that sometime a click on an item took more time than expected and I had no visual clue to see if it was loading or not.
-
Get current langauge in init.php or init method of module
Sergio replied to Zeka's topic in General Support
Ops! Sorry! Soma showed a way to get in on a module init method: -
Get current langauge in init.php or init method of module
Sergio replied to Zeka's topic in General Support
It is. // _init.php $lang = $user->language; //_main.php <?= $lang->title ?> //English or German etc. -
That's great, @rash!! The hyphens seems to be created by the escaping of the forward/back slashs on the file path, one hyphen replace each, so that's why I noticed that your _strings.php file appeared to have the wrong path.