Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 10/07/2016 in all areas

  1. So far things are going great for our new master version of ProcessWire, version 3.x. In this post, we'll take a look at what's new in ProcessWire 3.0.36 master, plus a look forward at what we'll be working on in the weeks ahead. https://processwire.com/blog/posts/processwire-3.0.36-and-looking-forward/
    7 points
  2. 4 points
  3. @szabesz - I had a play around to see how you could use custom PHP replacements in the description field. My first thought was create a module where you define tag/code pairs in the module config, then look for and replace those in descriptions. This works, but requires the use of eval() and conventional wisdom is that eval() is evil. So my next thought was Hanna Code, and this seems to work great. In ready.php (or create an autoload module): $this->addHookBefore('Inputfield::render', function($event) { if(!$this->wire('modules')->isInstalled('TextformatterHannaCode')) return; if(!($this->wire('process') && $this->wire('process')->className() == 'ProcessPageEdit')) return; $inputfield = $event->object; $description = $inputfield->description; if($description == '') return; $this->wire('modules')->TextformatterHannaCode->formatValue(new Page(), new Field(), $description); $inputfield->description = $description; }); Then set up the Hanna codes you want to use in description fields. If you want to get the page object for the page being edited then add this at the top of your Hanna code... if($process && $process->className() == 'ProcessPageEdit') { $p = $process->getPage(); } else { return "[Hanna code '$hanna->name' is not valid for this field]"; } return $p->url; // an example to return the URL to the page without scheme and hostname ...then access the page object as $p
    3 points
  4. Is your CKEditor inputfield set to less than 100% width? Or in a fieldset that is less than 100% width? If so the culprit is probably the height spacer JS. GitHub issue here, has never been completely fixed. Only solution for me is never set CKEditor field at less than 100% width.
    3 points
  5. Thank you to everyone! All of you all's help led me to the right place!
    2 points
  6. welcome to the club - nice comparison ! Remember to move this block of code into <head> though... <script type="application/ld+json"> .... </script>
    2 points
  7. I fail to see anything wrong there. The title tag looks fine to me (in FF, Chrome and Edge). <title>Surf Inn - holiday accommodation Cape St Francis</title> Perhaps just a case of a stale browser cache?
    2 points
  8. Thanks a lot @Robin S! Last night I went to bed and had a great sleep, and this morning when I wake up, reading your post I realize that my dreams came true I will have time to try it out in the evening, but looks rather straighforward. I agree with @adrian, it is really smart to use Hanna Code. I hope that this thingy here is not an issue anymore and just left open for some reason and even if it is still an issue, your way of using Hanna Code is not affected by this.
    2 points
  9. Nice @Robin S - I really ilke the Hanna Code approach - ultimate flexibility and so simple to implement!
    2 points
  10. A PageTable field will not normally output a hidden page either. See here. But you can use getUnformatted() to get unpublished or hidden pages. $result = $page->getUnformatted('my_pagetable_field')[0]; //or $result = $page->getUnformatted('my_pagetable_field')->first();
    2 points
  11. @Macrura Here's what's above the PW directives in my .htaccess. Notice that I'm pointing explicitly to a 403 html file, right below the 6G directives: ErrorDocument 403 /403.html Start of .htaccess: # 6G FIREWALL/BLACKLIST # @ https://perishablepress.com/6g/ # 6G:[QUERY STRINGS] <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{QUERY_STRING} (eval\() [NC,OR] RewriteCond %{QUERY_STRING} (127\.0\.0\.1) [NC,OR] RewriteCond %{QUERY_STRING} ([a-z0-9]{2000}) [NC,OR] RewriteCond %{QUERY_STRING} (javascript:)(.*)(;) [NC,OR] RewriteCond %{QUERY_STRING} (base64_encode)(.*)(\() [NC,OR] RewriteCond %{QUERY_STRING} (GLOBALS|REQUEST)(=|\[|%) [NC,OR] RewriteCond %{QUERY_STRING} (<|%3C)(.*)script(.*)(>|%3) [NC,OR] RewriteCond %{QUERY_STRING} (\\|\.\.\.|\.\./|~|`|<|>|\|) [NC,OR] RewriteCond %{QUERY_STRING} (boot\.ini|etc/passwd|self/environ) [NC,OR] RewriteCond %{QUERY_STRING} (thumbs?(_editor|open)?|tim(thumb)?)\.php [NC,OR] RewriteCond %{QUERY_STRING} (\'|\")(.*)(drop|insert|md5|select|union) [NC] RewriteRule .* - [F] </IfModule> # 6G:[REQUEST METHOD] <IfModule mod_rewrite.c> RewriteCond %{REQUEST_METHOD} ^(connect|debug|delete|move|put|trace|track) [NC] RewriteRule .* - [F] </IfModule> # 6G:[REFERRERS] <IfModule mod_rewrite.c> RewriteCond %{HTTP_REFERER} ([a-z0-9]{2000}) [NC,OR] RewriteCond %{HTTP_REFERER} (semalt.com|todaperfeita) [NC] RewriteRule .* - [F] </IfModule> # 6G:[REQUEST STRINGS] <IfModule mod_alias.c> RedirectMatch 403 (?i)(wp-admin|wp-content|wp-login) RedirectMatch 403 (?i)([a-z0-9]{2000}) RedirectMatch 403 (?i)(https?|ftp|php):/ RedirectMatch 403 (?i)(base64_encode)(.*)(\() RedirectMatch 403 (?i)(=\\\'|=\\%27|/\\\'/?)\. RedirectMatch 403 (?i)/(\$(\&)?|\*|\"|\.|,|&|&amp;?)/?$ RedirectMatch 403 (?i)(\{0\}|\(/\(|\.\.\.|\+\+\+|\\\"\\\") RedirectMatch 403 (?i)(~|`|<|>|:|;|,|%|\\|\s|\{|\}|\[|\]|\|) RedirectMatch 403 (?i)/(=|\$&|_mm|cgi-|etc/passwd|muieblack) RedirectMatch 403 (?i)(&pws=0|_vti_|\(null\)|\{\$itemURL\}|echo(.*)kae|etc/passwd|eval\(|self/environ) RedirectMatch 403 (?i)\.(aspx?|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rar|rdf)$ RedirectMatch 403 (?i)/(^$|(wp-)?config|mobiquo|phpinfo|shell|sqlpatch|thumb|thumb_editor|thumbopen|timthumb|webshell)\.php </IfModule> # 6G:[USER AGENTS] <IfModule mod_setenvif.c> SetEnvIfNoCase User-Agent ([a-z0-9]{2000}) bad_bot SetEnvIfNoCase User-Agent (archive.org|binlar|casper|checkpriv|choppy|clshttp|cmsworld|diavol|dotbot|extract|feedfinder|flicky|g00g1e|harvest|heritrix|httrack|kmccrew|loader|miner|nikto|nutch|planetwork|postrank|purebot|pycurl|python|seekerspider|siclab|skygrid|sqlmap|sucker|turnit|vikspider|winhttp|xxxyy|youda|zmeu|zune) bad_bot # Apache < 2.3 <IfModule !mod_authz_core.c> Order Allow,Deny Allow from all Deny from env=bad_bot </IfModule> # Apache >= 2.3 <IfModule mod_authz_core.c> <RequireAll> Require all Granted Require not env bad_bot </RequireAll> </IfModule> </IfModule> ErrorDocument 403 /403.html <FilesMatch "\.(js|css|html|htm|php|svg)$"> SetOutputFilter DEFLATE </FilesMatch> <ifModule mod_headers.c> Header set Connection keep-alive </ifModule> # ---------------------------------------------------------------------- # Expires headers (for better cache control) # ---------------------------------------------------------------------- <IfModule mod_expires.c> ExpiresActive on # Your document html ExpiresByType text/html "access plus 0 seconds" # Data ExpiresByType text/xml "access plus 0 seconds" ExpiresByType application/xml "access plus 0 seconds" ExpiresByType application/json "access plus 0 seconds" # Favicon (cannot be renamed) ExpiresByType image/x-icon "access plus 5 days" # Media: images, video, audio ExpiresByType image/gif "access plus 1 week" ExpiresByType image/png "access plus 1 week" ExpiresByType image/jpeg "access plus 1 week" ExpiresByType image/webp "access plus 1 week" ExpiresByType image/svg+xml "access plus 1 week" # HTC files (css3pie) ExpiresByType text/x-component "access plus 1 month" # Webfonts ExpiresByType application/x-font-ttf "access plus 1 month" ExpiresByType font/opentype "access plus 1 month" ExpiresByType application/x-font-woff "access plus 1 month" ExpiresByType application/vnd.ms-fontobject "access plus 1 month" # CSS and JavaScript ExpiresByType text/css "access plus 1 week" ExpiresByType application/x-javascript "access plus 1 week" </IfModule> ################################################################################################# # START PROCESSWIRE HTACCESS DIRECTIVES # @version 3.0 # @indexVersion 300 ################################################################################################# ....
    2 points
  12. @alexmercenary I published a new version 1.0.1 which adds namespace support.
    2 points
  13. Attention: please don't install this module at the time being! It is not compatible with current PW versions, and it will be some time until I can work in all the changes. Due to a discussion here in the forums, I was inspired to finally have a take on datetime fields and see if I couldn't get them to be searched a little more conveniently. Here's a small module - still in alpha state, but I'd be happy to get some feedback - that allows searching for individual components of a date like year, month, day, hour or even day_of_week or day_of_year, and also returning them. Github repo: DatetimeAdvanced Current version: 0.0.5 Tested in: ProcessWire 2.8 + 3.0 Possible subfields: day month year hour minute second day_of_week day_of_year week_of_year Examples: // Database search: $pagelist = $pages->find("mydatefield.year=2016"); // Filtering PageArray in memory: $maypages = $pagelist->filter("mydatefield.month=5"); // Back to our starting point: $start = date('z'); $end = $start + 7; $sevendays = $pages->find("mydatefield.day_of_year>=$start, mydatefield.day_of_year<$end"); // Nice side effect: subfields are now directly accessible $blogentry = $pages->get('blog-entry-1'); echo $blogentry->title . "(" . $blogentry->publishdate->year . ")"; // New in 0.0.4: shorthand methods echo $blogentry->publishdate->strftime("%Y-%m-%d %H:%M:%S") . PHP_EOL; echo $blogentry->publishdate->date("Y-m-d H:i:s") . PHP_EOL; ToDos for the future: See if there's a possibility to specify ranges more conveniently Check if this can perhaps wiggle its way into the PW core Changes: example for direct subfield access and shorthand methods to strftime() and date() added.
    1 point
  14. Hi, I'm making a new subscribers module, which enables you to register users, and view them as a list of the new role in the admin. Having trouble with installation currently and keep getting Invalid field type in call to Field::setFieldType module: https://github.com/benbyford/subscribers/blob/master/Subscribers.module
    1 point
  15. Welcome to the Forums! If it is 60% done, you are probably better off finishing it. However, if you have the time, you can start it from scratch and learn ProcessWire at the same time. Learning ProcessWire is a nice experience with a good, linear learning curve, but it is still something new to learn. For example the above mentioned "page/field/template strategy" depends on your needs and experience too, so this is something you need to examine in detail before you make the plunge. You can find a lot of topics on it in the forums and in the docs. If you want to save time on coding, look up the Modules Directory. I recommend modules with v2.7 and/or v3 compatibility. Always check out the module's forum topic to learn more about its current status and get more info on them. As for Yii2 or Laravel suggested by @Sephiroth, I do not think it is something everyone should follow, at least ProcessWire is perfectly suitable for "anything" and development can be quite rapid with it, one just needs the experience to build something quickly in any framework one uses.
    1 point
  16. Waooh problem solved. That was fast. Thanks a lot !!
    1 point
  17. Success! Try upgrading to version 021.
    1 point
  18. This will be my honest opinion Processwire works for this without a problem To be sincere this section I would advise using a framework like Yii2 or Laravel, if you are going to handle concepts like RBAC or Permission and authorization, as am assuming there would different roles and all. I tend to use Processwire for mostly content centric sites or mostly site management, but when it comes to applications with specific logic, I tend to use frameworks like Yii2/Symfony as i would love to have things as flexible as possible. Once again this is my personal opinion.
    1 point
  19. Think my tutorials are a good starter and start prietty basic. let us know if you think there are anything missing... e.g. if it would be worth making some screencasts etc https://tutsplus.com/authors/ben-byford
    1 point
  20. Hello DL7, Excellent that you are considering ProcessWire, it's an amazing piece of Software. All of this is possible without custom coding, it's all built into the CMF API. I can't say I've had much experience with custom database tables however I do know that ProcessWire has an API to deal with that too: https://processwire.com/api/ref/database/ Thanks, Tom
    1 point
  21. @ZGD The above way of encoding/decoding is only meant as an example - it will put carriage-returns and linefeeds anywhere there is a literal '\r' or '\n' in the data. So, if you have anything like a Windows path (C:\regarding\my\new\nested\filesystem) you could get C: egarding\my ew ested\filesystem out. You will need to pick substitution tokens (particularly for the "\n" newlines) that will not be in your data stream. I think you will get away without substituting the "\r" carriage returns.
    1 point
  22. Anybody feeling adventures could use my (still a bit experimental) DatetimeAdvanced field. With that, the code gets a little more concise. The retrieval of the oldest page can be optimized a little with the findOne method when using PW >= 3.0.0: $firstEntry = $pages->findOne("parent_id=1037, sort=date_1"); foreach (range(date("Y"), date("Y", $firstEntry->date_1)) as $year) { $count = $pages->count("parent_id=1037, date_1.year=$year"); if ($count > 0) echo '<li><a href="' . $page->parent->url . $year . '/">' . $year . '(' . $count . ')</a></li>'; }
    1 point
  23. Is imagefield set to return a single image? This looks like you're accessing the data from a Pagefiles object. In that, case, this should work: echo $site->pages->get(pageid)->imagefield->first()->httpUrl;
    1 point
  24. At least for links to pages, you can enable the builtin link abstraction feature that came with PW 3.0.6. I haven't used it myself (wrote my own link abstraction shortly before 3.0.6 came out and mine ties into other modules/workflows I use), but from what I saw in the code, it should deal with any path changes on its own. You're left to handle images yourself though (I think).
    1 point
  25. I've got a persistent case of this: I can login AOK if I use an incognito window sessions are stored in the db I've tried clearing out my browser history, cache etc, but clearly PW is now remembering something about my normal browser and not liking it. Is my only option to permanently change "CSRF protection" and "session fingerprints" settings (which is sad as I used to login here AOK and didn't need them changed before), or, is there something I can do on the hosting or in PW to clear out PW's memory of my browser (assuming that is the problem), as noted, I *can* login with incognito. Thanks friends and sorry if I missed something obvious (and hope the answer helps someone else :)) EDIT Even though sessions are stored in the PW database, I checked /site/assets/sessions and found two session files o_O Deleted them and now can login OK. Confused but happy
    1 point
  26. I had a similar problem to this when I wrote a module for a client. It turned out that having a tab open on the module's log page stopped the hook from firing, even though I had a cron job, running every minute, visiting a page on the site. As far as I can tell, the auto refresh that occurs on the log pages somehow stops the hook from triggering. Once I closed the log view page, I could watch things go back to normal just be looking at the actual log file from the command line using the 'tail -f' command. The solution for me was to only open the log pages when needed and provide my own view of the log tail as part of the module's settings page. HTH
    1 point
  27. I've always used a subdomain instead of a directory. You don't get the added path problems. The times I have had to work out of a subdirectory I've used something like phpmyadmin or adminer to change the links before shifting everything over.
    1 point
  28. In case anyone finds this thread because they're deploying a project started in PW2.7 or older to a ServerPilot-managed container (in which case, just updating the schema isn't sufficient, as many records may have been created with invalid values), ServerPilot recommends creating a separate .cnf file to disable sql strict mode: https://serverpilot.io/community/articles/how-to-disable-strict-mode-in-mysql-5-7.html Don't just copy and paste that configuration, though. The specific items that need to be removed are NO_ZERO_IN_DATE and NO_ZERO_DATE. Creating a separate .cnf file for your customizations will avoid them being overwritten by automated ServerPilot updates.
    1 point
  29. That should be all you need. Please try it out and see for yourself.
    1 point
  30. Welcome to the ProcessWire Frorum @leegold a Step by step tutorial: http://blog.mauriziobonani.com/tags/#processwire Basic ProcessWire website workflow - Part Four Basic ProcessWire website workflow - Part Three Basic ProcessWire website workflow - Part Two Basic ProcessWire website workflow - Part One
    1 point
  31. It all make sense now! Thank you for guys for clarification.
    1 point
  32. thanks for correcting me; i was going by what I've seen on the majority of sites, as well as the HTML5 boilerplate, so i was blindly following the crowd, and i think google changed their instructions at some point also.
    1 point
  33. I don't think so, no. The idea above was to replace any embedded ASCII carriage-return+linefeed pairs with a literal sequence '\' + 'r' + '\' + 'n' on save of the data. On reading the data the, now-literal, sequence is turned back into an embedded ASCII pair. If the ASCII linefeed and return characters can occur anywhere - not just next to each other - then you need to replace them individually. Try doing this instead... protected function encodeData($data) { $data = serialize($data); $data = str_replace("\r", '\r', $data); $data = str_replace("\n", '\n', $data); return $data; } protected function decodeData($data) { $data = str_replace('\r', "\r", $data); $data = str_replace('\n', "\n", $data); $data = unserialize($data); return $data; } public function addItem($arrayData) { if(!$this->_addItem()) return false; if(2 != $this->getState()) return false; if(!$fp = @fopen($this->getFilename(), 'ab')) return false; if(flock($fp, LOCK_EX)) { $data = $this->encodeData($arrayData) . "\n"; $res = fwrite($fp, $data); fflush($fp); flock($fp, LOCK_UN); fclose($fp); return $res == strlen($data); } fclose($fp); return false; } public function getItem($worker = null) { if(!$this->_getItem()) return false; if(2 != $this->getState()) return false; if(!$fp = @fopen($this->getFilename(), 'rb+')) return false; if(flock($fp, LOCK_EX)) { $line = trim(fgets($fp)); if(!$line) { flock($fp, LOCK_UN); fclose($fp); if(0 == $this->itemCount()) return null; return false; } // we have the first entry, now write all following data into a buffer $fpTmp = @fopen('php://temp/maxmemory:' . intval(1024 * 1024 * 5), 'rb+'); while(!feof($fp)) fwrite($fpTmp, fread($fp, 4096)); fseek($fp, 0, SEEK_SET); ftruncate($fp, 0); fseek($fpTmp, 0, SEEK_SET); // write back buffer into file while(!feof($fpTmp)) fwrite($fp, fread($fpTmp, 4096)); fclose($fpTmp); fflush($fp); flock($fp, LOCK_UN); fclose($fp); } return $this->decodeData($line); } Sorry, I don't really have time to test this out - you'll have to play with the encoding. You may find that doing the serialisation at the end of the encode (and unserialisation at the start of decode) might do it. HTH
    1 point
  34. In this case, Google recommends just in the head, or after the opening body tag:
    1 point
  35. usually you put google analytics in the footer since it is a script tag
    1 point
  36. Welcome to the forums, @leegold ! I think you're going to like the tutorials by @benbyf, published on https://tutsplus.com/authors/ben-byford
    1 point
  37. Hi John and welcome to PW! To expand on what Tom said, you might be looking for a head.php template file, or perhaps it will be in main.php or something along those lines. PW doesn't control your frontend markup at all, so there is no way to know for certain how the previous developer set things up, but what you are looking for a a template file that is included in all the page specific template files, or one that itself includes the other ones.
    1 point
  38. Same place you would any other website, in the <head> part of your website. You will likely find this in /site/templates/ Thanks, Tom
    1 point
  39. I admit I haven't thought too much about nesting, and the wiring around the field's json data would obviously need to be completely different. InnoDB does support searching in nested structures, but PW already treats subfields on the left side of a selector in its own way and throws away anything starting at the second full stop. A different syntax would be necessary, e.g. $pages->find('jsonField=/subField/field:foo') There might be issues where search expressions need some kind of escaping (e.g. searching for literal text "/subfield"), but this would allow for wildcard queries. $pages->find('jsonField=/subField/*/name:foo') Or even searching arrays (any element or by index): $pages->find('jsonField=/subField/fields#*/name:foo'); $pages->find('jsonField=/subField/field#12/name:foo'); Here's a tiny adoption of my module to allow this search syntax. There's no back and forth conversion though, just a textarea containing the JSON. <?php class FieldtypeJsonDocument extends FieldtypeTextarea implements Module { public static function getModuleInfo() { return array( "title" => "Fieldtype Json Document", "summary" => "Fieldtype utilizing native MySQL support for searching JSON documents.", "version" => "0.0.3", ); } public function getMatchQuery($query, $table, $subfield, $operator, $value) { $database = $this->wire("database"); list($path, $value) = explode(':', $value, 2); if(empty($value) && !empty($path)) { $value = $path; $path = ""; } $path = '$' . ((empty($subfield) || $subfield == "data") ? "" : ".$subfield") . (empty($path) ? ".*" : str_replace('/', '.', preg_replace('~/?#(\d+|\*)~', "[$1]", $path))); $table = $database->escapeTable($table); $value = $database->escapeStr($value); if($operator == "=") { $query->where("JSON_SEARCH({$table}.data, 'one', '$value', NULL, '$path') IS NOT NULL"); } else if($operator == "*=" || $operator == "%=") { $query->where("JSON_SEARCH({$table}.data, 'one', '%$value%', NULL, '$path') IS NOT NULL"); } else if($operator == "^=") { $query->where("JSON_SEARCH({$table}.data, 'one', '$value%', NULL, '$path') IS NOT NULL"); } else if($operator == "$=") { $query->where("JSON_SEARCH({$table}.data, 'one', '%$value', NULL, '$path') IS NOT NULL"); } $this->log->message($query->getQuery()); return $query; } public function getDatabaseSchema(Field $field) { $engine = $this->wire('config')->dbEngine; $charset = $this->wire('config')->dbCharset; $schema = array( 'pages_id' => 'int UNSIGNED NOT NULL', 'data' => "JSON", // each Fieldtype should override this in particular 'keys' => array( 'primary' => 'PRIMARY KEY (`pages_id`)', ), // additional data 'xtra' => array( // any optional statements that should follow after the closing paren (i.e. engine, default charset, etc) 'append' => "ENGINE=$engine DEFAULT CHARSET=$charset", // true (default) if this schema provides all storage for this fieldtype. // false if other storage is involved with this fieldtype, beyond this schema (like repeaters, PageTable, etc.) 'all' => true, ) ); return $schema; } public function getInputfield(Page $page, Field $field) { $inputField = $this->modules->get('InputfieldTextarea'); return $inputField; } public function install() { if($this->config->dbEngine != "InnoDB") { throw new WireException($this->_("InnoDB database engine needs to be used for native JSON support")); } $dbver = $this->database->getAttribute(PDO::ATTR_SERVER_VERSION); if(version_compare($dbver, '5.7.8', '<')) { throw new WireException(sprintf($this->_("MySQL Server version needs to be at least 5.7.8 for fully working JSON support, installed version is %s"), $dbver)); } } } There would probably be a bit of recursive shuffling necessary in sleep-/wakeup-/sanitizeValue to build nested structures based on WireData/WireArray so accessing subfields and filtering PageArrays is possible, but that should be doable too. How difficult the backend input + display parts get depends solely on your exact requirements.
    1 point
  40. Just a short update: if MySQL (or the OS it's running on) and PHP are configured with different time zones, results will be wrong. You'll likely not notice anything off on local dev environments where everything is configured with the same time zone, but to prevent issues on deployment, MySQL needs to be made timezone-aware and PW's database connections need to tell MySQL which timezone to use for conversions, which should IMHO be done through a not-yet-existing $config property. That's why I've filed feature request #19. I'm now holding my fingers crossed and waiting to hear if it gets considered. This will mean that, assuming it does, the module will require at least the PW version that gets the new $config property.
    1 point
  41. Am back in play. I am working on the frontend module.
    1 point
  42. jQuery attr seems good to me. Another option may be str_replace on the output something like $out = $nav->render(); $out = str_replace('class="has_children"', 'class="has_children" data-uk-dropdown', $out);
    1 point
  43. To get a page by index number from a PageArray is easy: /* returns a page object */ $pagebyindex = $myparentpage->children->eq(integer $num); Now I needed a function which returns the Index of the page from the siblings. It took me a while to figure this out, and I like to share this here: /* returns an integer */ $indexbypage = $myparentpage->children->getItemKey($page); // or $indexbypage = $page->siblings->getItemKey($page); Found this information in http://cheatsheet.processwire.com/ under section PageArray/WireArray and here: http://processwire.com/apigen/ To get complete information about API this page is your (and my) friend. Have a nice day
    1 point
  44. coming back to this: I have made good progress! I do not use the htaccess file in site/assets/files/ anymore but have edited the htaccess file in pw root folder. Somewhere at top of the mod_rewrite directives I have added my lines that should redirect requests to original images to a proxy-script and let others pass through: htaccess with Pim1 and PW < 2.5.11 .htaccess with PW 2.5.11+ / PW 3+ # ----------------------------------------------------------------------------------------------- # CUSTOMSETTING : redirect original images to proxy-script - /pwimg.php?fn=... # ----------------------------------------------------------------------------------------------- RewriteCond %{REQUEST_FILENAME} -f RewriteCond %{REQUEST_FILENAME} (^|/)site/assets/files/(.*?)/ RewriteCond %{REQUEST_FILENAME} \.(jpg|jpeg|gif|png)$ [NC] RewriteCond %{REQUEST_FILENAME} !-piacrop RewriteCond %{REQUEST_FILENAME} !-piacontain RewriteCond %{REQUEST_FILENAME} !-pim2-full RewriteCond %{REQUEST_FILENAME} !-blogthumb RewriteCond %{REQUEST_FILENAME} !.*/.*?\.([0-9]+)x([0-9]+)\.(jpg|png|jpeg|gif)$ [NC] RewriteRule ^(.*)$ pwimg.php?fn=$1 [L] Now from all existing images the originals get redirected to the proxy-script and the others will delivered directly by apache. Requests to none existing imagefiles get answered by a 404. So as everything seems to work fine, the RewriteConditions could be optimized a bit. ---- pwimg.php ---- <?php // check filename $imgFilename = isset($_GET['fn']) ? preg_replace('/[^a-zA-Z0-9_\-\/\.@]/', '', $_GET['fn']) : false; $imgFilename = is_file(dirname(__FILE__) . "/$imgFilename") && is_readable(dirname(__FILE__) . "/$imgFilename") ? dirname(__FILE__) . "/$imgFilename" : false; if (false == $imgFilename) { header('HTTP/1.1 404 Not Found'); exit(2); } // check imagetype $imgType = getImageType($imgFilename); if (false == $imgType) { header('HTTP/1.1 403 Forbidden'); header('Content-type: image/jpeg'); exit(1); } // bootstrap PW require_once(dirname(__FILE__) . '/index.php'); // check user-account if (! wire('user')->hasRole('superuser|editor')) { header('HTTP/1.1 403 Forbidden'); header('Content-type: ' . $imgType); exit(1); } // collect infos $maxAge = (60 * 60 * 2); // 2 hours $imgTimestamp = filemtime($imgFilename); $imgExpiration = intval(time() + $maxAge); // create headers $imgHeaders = array(); $imgHeaders[] = 'Content-type: ' . $imgType; $imgHeaders[] = 'Content-Length: ' . filesize($imgFilename); $imgHeaders[] = 'Date: ' . gmdate('D, d M Y H:i:s',time()) . ' GMT'; $imgHeaders[] = 'Last-Modified: ' . gmdate('D, d M Y H:i:s',$imgTimestamp) . ' GMT'; $imgHeaders[] = 'Expires: ' . gmdate('D, d M Y H:i:s', $imgExpiration) . ' GMT'; $imgHeaders[] = 'pragma: cache'; $imgHeaders[] = "Cache-Control: no-transform, private, s-maxage={$maxAge}, max-age={$maxAge}"; // send headers foreach($imgHeaders as $imgHeader) header($imgHeader); // send file $errorCode = @readfile($imgFilename) === FALSE ? 1 : 0; // and exit exit($errorCode); // --- functions --- function getImageType($fn, $returnAsInteger = false) { $types1 = array(1 => 'gif', 2 => 'jpg', 3 => 'png'); $types2 = array('gif' => 1, 'jpg' => 2, 'jpeg' => 2, 'png' => 3); if (function_exists('exif_imagetype') && isset($types1[@exif_imagetype($fn)])) { $success = $types1[exif_imagetype($fn)]; } if (!isset($success) && function_exists('getimagesize')) { $info = @getimagesize($fn); if (isset($info[2]) && isset($types1[$info[2]])) { $success = $types1[$info[2]]; } } if (!isset($success)) { $extension = strtolower(pathinfo($fn, PATHINFO_EXTENSION)); if (isset($types2[$extension])) { $success = $types1[$types2[$extension]]; } } if (!isset($success)) return false; return true === $returnAsInteger ? $types2[$success] : $success; }
    1 point
×
×
  • Create New...