-
Posts
1,331 -
Joined
-
Last visited
-
Days Won
61
Everything posted by BitPoet
-
That's trashing them (the tab title is unfortunately misleading there). The pages are put into trash and only deleted when you empty the trash.
-
The hook looks fine to me. Are you in fact deleting the page or only trashing it?
-
Catchable fatal error: session_regenerate_id()
BitPoet replied to Jonathan Sachse Mikkelsen's topic in General Support
The error message reads like Memcached session handler is either unable to reach the server nfs01.cl2000.ams1.nl.leaseweb.net or to read from it. If you're on shared hosting, you should probably forward the error to your provider and let them take a look. -
Catchable fatal error: session_regenerate_id()
BitPoet replied to Jonathan Sachse Mikkelsen's topic in General Support
Is your memcached deamon down or hanging? -
Do that. Rely on the built-in mechanisms for user authentication and authorization as much as you can, and you have already eliminated three quarters of the security pitfalls of writing a client app. Let your app deal with regular http status codes like 401 instead of custom status messages and you have another ten percent.
-
If you're really importing into an empty database, this should have worked. If you run the following statement in phpMyAdmin on your server, do you get any table names back? /* Replace $YOUR-DATABASE-HERE$ with the real name of your database */ SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = '$YOUR-DATABASE-HERE$' AND CAST(TABLE_NAME AS BINARY) RLIKE '[A-Z]';
-
Best would be to check "Export tables as separate files", then you can select which table files to transfer (or extract from zip) to your local computer. Skip any tables with uppercase characters, then import the remaining (all lowercase) tables. In any case, do not check the "IF NOT EXISTS" box or you might end with old or duplicate data in your tables.
-
Are you transferring from Linux to Windows or MacOS? It looks like you have table names that are different in upper-/lowercasing but otherwise identical. MySQL makes distinctions there on Linux but treats upper and lower case the same on other OSes, depending on whether the underlying file system is case sensitive (here's some more information on that topic). If that's the case, your best bet would probably be to do a selective database export and only export the tables that are all lowercase, then the import should run fine. There's a miniscule possibility that some valid data is contained in the uppercase tables, but I'd only start looking there if something is actually missing.
-
As threatened in the Pub sub forum in the "What are you currently building?" thread, I've toyed around with Collabora CODE and built file editing capabilities for office documents (Libre-/OpenOffice formats and MS Office as well as a few really old file types) into a PW module. If you are running OwnCloud or NextCloud, you'll perhaps be familiar with the Collabora app for this purpose. LoolEditor Edit office files directly in ProcessWire Edit your docx, odt, pptx, xlsx or whatever office files you have stored in your file fields directly from ProcessWire's page editor. Upload, click the edit icon, make your changes and save. Can be enabled per field, even in template context. Currently supports opening and saving of office documents. Locking functionality is in development. See the README on GitHub for installation instructions. You should be reasonably experienced with configuring HTTPS and running docker images to get things set up quickly. Pull requests are welcome! Here is a short demonstration:
- 11 replies
-
- 19
-
-
-
Everybody's currently hyping about Collabora CODE and editing OpenOffice, MS Office and PDF documents inside their Own-/NextCloud. Recently, a colleague wanted to know if it was possible to do that in our corporate intranet too. And so I thought, why not? So I have started dabbling with NextCloud and the Collabora docker image a bit and, after banging my head against the wall for a while (figuratively) and doing a lot of reverse engineering to wrap my mind around the wopi and lool stuff, I managed to include the LOOL leaflet in a page of my own making and load and save documents in it through a small PHP script. I'm starting to create a PW module from this that allows inline (more precisely: modal) editing of supported document types. The next steps: A small file abstraction layer that converts back and forth between WOPI-style file ids necessary for LOOL and PW file urls, provides locking and checks access permissions A file access token generator / verifier A WOPI REST endpoint library that handles all the actions needed for opening, saving, locking etc. and provides auxiliary information like user info, avatar etc. Finally, the module itself, containing both the WOPI endpoint logic that the Collabora image uses and the UI extension for InputfieldFile that creates the editor I'm going to get the basics set up, then I'll whip up a getting started manual that also documents the "missing links" that I had so much trouble with and finally push this and the code to github so anybody interested can contribute too.
-
@kongondo's example is exactly what I meant. It's unfortunately a bit hidden in the jQuery api docs, but the third example in the $(document).ready() docs illustrates that the ready handler is passed the jQuery object.
-
Perhaps the script runs too early. In that case, wrap the code in a document ready handler.
-
Found the reason. getQueryUnknownField is far younger than I expected. It was added in preparation for the field.owner.subfield syntax in 3.0.87 which was officially introduced in 3.0.95.
-
It's a quick&dirty modification of a selector extension I whipped up out of curiosity a while ago. I'll take a look at it over the weekend.
-
Adds a soundex.field=something search option (so no custom operator) for database queries (so in your case, you'd use a selector like "soundex.firstname=$firstname, soundex.lastname=$lastname): <?php // Add to site/ready.php wire()->addHookAfter("PageFinder::getQueryUnknownField", function(HookEvent $event) { static $n = 0; $fieldName = $event->arguments(0); $data = $event->arguments(1); // We only handle our soundex queries if($fieldName != "soundex") return; if(empty($data["subfield"])) { throw new PageFinderSyntaxException("soundex selector expects the name of the field to search as subfield, e.g. 'soundex.title=something'"); } $selectors = $data["selectors"]; $selector = $data["selector"]; $query = $data["query"]; if(! $selector instanceof SelectorEqual) { throw new PageFinderSyntaxException("Invalid operator. Only '=' is allowed in soundex selectors"); } $subfield = "data"; $fieldName = $data["subfields"]; if(strpos($fieldName, '.') > 0) list($fieldName, $subfield) = explode('.', $fieldName); else $fieldName = $data["subfield"]; $field = wire('fields')->get($fieldName); if(!$field) { return; // does not resolve to a known field } // Initialize some needed variables $database = wire('database'); $table = $database->escapeTable($field->getTable()); $subfield = $database->escapeCol(wire('sanitizer')->fieldName($subfield)); $n++; $tableAlias = "_subquery_{$n}_$table"; $join = "$table AS $tableAlias ON $tableAlias.pages_id=pages.id"; $where = "$tableAlias.$subfield SOUNDS LIKE " . $database->quote($selector->value); $query = $data["query"]; $query->join($join); $query->where($where); $selectors->remove($selector); $event->return = true; });
-
ModuleManager inherits from the Process class and calls its parent's init method here. Process::init in turn invokes Modules::loadModuleFileAssets. This method looks for .js and .css files in the same location and with the same name as the module it is called for and, if found, adds them to $config->scripts / $config->styles. Admin themes all have lines like these in their template code to load the contents of the $config->scripts and $config->styles: <?php foreach($config->styles as $file) echo "\n\t<link type='text/css' href='$file' rel='stylesheet' />"; ?> <?php foreach($config->scripts as $file) echo "\n\t<script type='text/javascript' src='$file'></script>"; ?>
-
ProcessWire shouldn't need 777 at all. You just need to make sure that the web server can write in site/assets, since this is where all dynamically changing data (caches, files, logs) is stored. You probably just need to give ownership to www-data (or whatever the webserver runs as) and can switch to 0750. If you're worried about security: the .htaccess file is quit strict in limiting access there.
-
Is it possible to use HTML tags in log files and how?
BitPoet replied to Juergen's topic in General Support
Or you can steal one or two methods @ryan's code, stuff them into your little module, tweak a few lines and add even more fun to PW Attached is a quick & dirty little module (actually, two of them) that adds: $log->mailLog($your, $fields, $ending, $with, ..., $messagebody) [pass as many arguments as you like; the last one will always have white-space set to wrap] a special log viewer at Setup -> Mail Log that displays as many columns as you sent to mailLog() It uses some unlikely to naturally occur text patterns for newlines and field separators and fools ProcessLogger::executeView into thinking it is pointed to the mail log, and that way lets it do the heavy lifting. ProcessMailLogger.zip- 10 replies
-
- 10
-
-
input field type "select" cannot remove "parent" once selected
BitPoet replied to neosin's topic in General Support
Just click on the "Change" button and hover over the currently selected page in the tree. The "unselect" button will pop out next to it. It's a bit hard to find, especially using the UIKit admin theme. -
Glad to hear that helped. What I meant regarding saved/added: the saveReady and saved hooks are executed every time you save a book page, even if it is just to amend some information. The created timestamp won't change anymore after the first save when it was added. The "added" hook will only execute once for each book page at the time it is created, so you can avoid running your code each time. On saved/added vs. saveReady: there might be an (unlikely, but not impossible) error where something prevents saving your book page. If that happens and you use saveReady, your parent page's lastcreated field will have been updated nonetheless, which will lead to an inconsistency until the next book page is added and correctly saved so it overwrites its lastcreated field again.
-
If that's the exact code, you have an error. It should read "$p->parent->lastcreated", not "$parent->lastcreated". Perhaps that's the whole cause. I'd put the code in a "saved" (or even better "added", since the created property isn't changed after the first save) hook though, so that updating the parent only happens after successfully saving the book page. <?php $pages->addHookAfter('added', function($event) { $p = $event->arguments(0); if($p->template->name == 'book') { $parent = $p->parent; $lastcreated = $parent->lastcreated; if($p->created > $lastcreated) { $parent->lastcreated = $p->created; } // Save just the lastcreated field and leave modified user and time alone $parent->save("lastcreated", ["quiet" => true]); } });
-
It's a bit on the back burner right now. I was hoping for some more elaborate possibilities with MySQL 8, but the changes to JSON support there were on the homeopathic end rather than the dynamic typing support I had hoped for. I plan to brush up the UI and client side validation a bit once PW 3.1 gets out, though, and test things out in depth with UIkit admin theme.
-
Definitely. Thank for pointing that out, I have ammended my post.
-
Not sure where the best place for this is, but I felt like sharing a little snippet for site/ready.php I wrote that creates a PageArray::groupBy method: <?php /** * * Adds a groupBy method to all PageArray instances. * * Returns a nested array, with the values of the page properties whose names * were passed as arguments as the keys. * * Usage: * ====== * * $grouped = $mypagearray->groupBy(field1 [, field2 ...] [, mutator_function]); * or * $grouped = $mypagearray->groupBy(mutator_function); * * Example: * ======== * * $mypagearray = $pages->find("template=blog-post, sort=year, sort=month"); * $grouped = $mypagearray->groupBy("year", "month"); * * foreach($grouped as $year => $monthgroup) { * echo "<div class='year'><h2>$year</h2>" . PHP_EOL; * echo "\t<ul class='month'>" . PHP_EOL; * * foreach($monthgroup as $month => $mypages) { * echo "\t\t<li><h3>$month</h3>" . PHP_EOL; * * echo "\t\t\t<ul class='post'>" . PHP_EOL; * * foreach($mypages as $post) { * echo "\t\t\t\t<li><a href='{$post->url}'>{$post->title}</a></li>" . PHP_EOL; * } * * echo "\t\t\t</ul>\n" . PHP_EOL; * echo "\t\t</li>\n" . PHP_EOL; * } * * echo "\t</li>" . PHP_EOL; * echo "</ul>" . PHP_EOL; * } * * Example Output: * =============== * * <div class='year'><h2>2016</h2> * <ul class='month'> * <li><h3>1</h3> * <ul class='post'> * <li><a href='/grouptest/grouptest-10/grouptest-10-10/'>Group Test 10 10</a></li> * <li><a href='/grouptest/grouptest-6/grouptest-6-10/'>Group Test 6 10</a></li> * <li><a href='/grouptest/grouptest-10/grouptest-10-12/'>Group Test 10 12</a></li> * <li><a href='/grouptest/grouptest-1/grouptest-1-12/'>Group Test 1 12</a></li> * <li><a href='/grouptest/grouptest-5/grouptest-5-3/'>Group Test 5 3</a></li> * <li><a href='/grouptest/grouptest-10/grouptest-10-4/'>Group Test 10 4</a></li> * <li><a href='/grouptest/grouptest-3/'>Group Test 3</a></li> * <li><a href='/grouptest/grouptest-6/grouptest-6-4/'>Group Test 6 4</a></li> * <li><a href='/grouptest/grouptest-10/grouptest-10-7/'>Group Test 10 7</a></li> * </ul> * * </li> * * <li><h3>2</h3> * <ul class='post'> * <li><a href='/grouptest/grouptest-5/grouptest-5-10/'>Group Test 5 10</a></li> * <li><a href='/grouptest/grouptest-3/grouptest-3-7/'>Group Test 3 7</a></li> * <li><a href='/grouptest/grouptest-9/grouptest-9-5/'>Group Test 9 5</a></li> * <li><a href='/grouptest/grouptest-7/grouptest-7-12/'>Group Test 7 12</a></li> * <li><a href='/grouptest/grouptest-3/grouptest-3-11/'>Group Test 3 11</a></li> * <li><a href='/grouptest/grouptest-9/grouptest-9-11/'>Group Test 9 11</a></li> * </ul> * * </li> * * <li><h3>3</h3> * <ul class='post'> * <li><a href='/grouptest/grouptest-7/grouptest-7-10/'>Group Test 7 10</a></li> * <li><a href='/grouptest/grouptest-12/grouptest-12-12/'>Group Test 12 12</a></li> * <li><a href='/grouptest/grouptest-11/grouptest-11-5/'>Group Test 11 5</a></li> * </ul> * * </li> * * <li><h3>4</h3> * <ul class='post'> * <li><a href='/grouptest/grouptest-8/grouptest-8-3/'>Group Test 8 3</a></li> * <li><a href='/grouptest/grouptest-12/grouptest-12-6/'>Group Test 12 6</a></li> * </ul> * * </li> * * </li> * </ul> * * IMPORTANT! * ========== * * Your PageArray needs to be sorted by the fields you group by, or your return array will be an * unorderly mess (the grouping is still correct, but the order of keys is arbitrary). * * Mutator Function: * ================= * * Instead of just reading properties from the page, you can also pass a mutator function * to groupBy. It gets passed the page object as its first arguments and any optional property * names after that. * * This might come in handy if you want to group by year and month but only have a single * DateTime field. You can then use a grouping function like this: * * $blogposts = $pages->find("template=blog-post, sort=created"); * $grouped = $blogposts->groupBy(function($pg) { * return array(strftime('%Y', $pg->created), strftime('%m', $pg->created)); * }); * */ wire()->addHook("PageArray::groupBy", function(HookEvent $event) { $out = array(); $args = $event->arguments(); if(count($args) == 0) throw new InvalidArgumentException("Missing arguments for function PageArray::groupBy, at least 1 required!"); $last = count($args) - 1; $fnc = is_string($args[$last]) ? FALSE : array_pop($args); foreach($event->object as $pg) { if($fnc) { $props = call_user_func_array($fnc, array_merge(array($pg), $args)); } else { $props = array_map(function($propname) use($pg) { return $pg->{$propname}; }, $args); } $cur = &$out; foreach($props as $prop) { if(!isset($cur[$prop])) $cur[$prop] = array(); $cur = &$cur[$prop]; } $cur[] = $pg; } $event->return = $out; });
- 4 replies
-
- 15
-
-