Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 09/26/2012 in all areas

  1. Well I sort of understand, I mean I'm still using VIM and a 1986 IBM Model-M keyboard. Those maybe those aren't good comparisons. I see us requiring 5.3 within the next 6-months or so, not 2-3 years. But at the moment, my main interest is just in growing our user base, so anything that contracts (rather than expands) the user base is undesirable. But I think 6-months from now will probably be the right time to do it.
    2 points
  2. Visually delineated blockquotes: I always like it when the pay-back from an investment in time such as adding custom styles into the TinyMCE editor (courtesy of Soma) jumps up a notch from just a tiny bit of code. Adding this blockquote { border-left: 1px dashed #ccc; border-right: 1px dashed #ccc; position: relative; padding: 0 0.38em; margin-left: 6.2em; margin-right: 3.8em; background: #f8f8f8; } blockquote:before { content: "blockquote"; position: absolute; top: 0; left: -5.2em; color: #bbb; border-top: 1px dashed #ccc; padding: 0.1em 0.2em 0 0; } to /site/tinymce/content.css gives your editors on-the-fly clear cues where `blockquotes` begin and end. In the I am using Cmd+e to add/remove `blockquote`, in the same way you can use Cmd+2 for H2 etc (the styling is deliberately 'light' so as not to get in the way, viewing in HD fullscreen shows it roughly as you would see it).
    2 points
  3. This seems to be working very well. Simplified the code a lot! Here is the current version: <?php class CustomPageRoles extends WireData implements Module { /** * Provide information about this module to ProcessWire * */ public static function getModuleInfo() { return array( 'title' => 'Custom Page Roles', 'summary' => 'Control viewable and editable roles at the page level.', 'version' => 002, 'permanent' => false, 'autoload' => true, 'singular' => true, ); } /** * Add the hook * */ public function init() { $this->addHookAfter("Page::viewable", $this, 'viewable'); $this->addHookAfter("Page::editable", $this, 'editable'); $this->addHookAfter("Page::addable", $this, 'addable'); //$this->addHookAfter("Pages::find", $this, 'hookPagesFind'); $this->addHookAfter("ProcessPageEdit::buildForm", $this, 'hookBuildForm'); } public function hookBuildForm(HookEvent $event) { if(wire('user')->hasRole('paakayttaja')) return; if(wire('user')->hasRole('superuser')) return; $form = $event->return; $field = $form->get('view_roles'); if($field) $field->collapsed = Inputfield::collapsedHidden; $field = $form->get('edit_roles'); if($field) $field->collapsed = Inputfield::collapsedHidden; $event->return = $form; } public function hookPagesFind(HookEvent $event) { $items = $event->return; foreach($items as $item) { if($item->template->flags & Template::flagSystem) { // don't allow this access control on system templates } else if (!$item->viewable()) { $items->remove($item); } } $event->return = $items; } /** * Hook called after Page::viewable is called * */ public function viewable(HookEvent $event) { // get the vars we need to check access $page = $event->object; $user = $this->user; // no need to check anything if it's the superuser if($user->isSuperuser()) return; // don't allow this access control on system templates if($page->template->flags & Template::flagSystem) return; if ($page->is(Page::statusUnpublished)) return; // get the roles we'll be comparing $pageRoles = $page->view_roles; $userRoles = $this->user->roles; // If there ain't no view_roles, then fallback to template permissions if (!$pageRoles) return; // if page has a 'view_roles' field, but none defined, then inherit 'viewable' state from parent if(!count($pageRoles)) { // determine access from page's parent, recursively // We check if there is at least one parent with edit_roles foreach($page->parents() as $p) { if (count($p->view_roles)) { $event->return = $p->viewable(); return; } } // If not view_roles found from parents, then fallback to template access return; } // we assume it's not viewable until we can prove otherwise $viewable = false; // loop through the pageRoles and try to match up to user // we don't need to check permissions in the role because // all roles are assumed to have page-view permission. foreach($pageRoles as $role) { if($role->name == 'guest' || $user->hasRole($role)) { // guest role was found or the user has the required role $viewable = true; } } $event->return = $viewable; } public function editable(HookEvent $event) { // get the vars we need to check access $page = $event->object; $user = $this->user; // no need to check anything if it's the superuser if($user->isSuperuser()) return; // don't allow page based access control on system templates if($page->template->flags & Template::flagSystem) return; // get the roles we'll be comparing $pageRoles = $page->edit_roles; $userRoles = $this->user->roles; // if page has a 'edit_roles' field, but none defined, then inherit 'editable' state from parent(s) if(!count($pageRoles)) { // We check if there is at least one parent with edit_roles foreach($page->parents() as $p) { if (count($p->edit_roles)) { $event->return = $p->editable(); return; } } // If not edit_roles found from parents, then fallback to template access return; } // we assume it's not editable until we can prove otherwise $editable = false; // loop through the pageRoles and try to match up to user // we don't need to check permissions in the role because // all roles are assumed to have page-view / page-edit permission. => actually we should disallow selecting other than roles which have page-edit in first place foreach($pageRoles as $role) { if ($user->hasRole($role)) { $editable = true; break; } } // This would be a setting, a "master role" for client if ($user->hasRole("paakayttaja")) $editable = true; $event->return = $editable; } /** * Add children permission is granted together with edit, at least for now * */ public function addable(HookEvent $event) { $page = $event->object; // If page is editable and generally can have children, then allow addable if ($page->editable() && !$page->template->noChildren) { $event->return = true; } else { $event->return = false; } } /** * Install the module * */ public function ___install() { if($this->fields->get('view_roles')) { $this->error("You already have a 'view_roles' field."); return; } if($this->fields->get('edit_roles')) { $this->error("You already have a 'edit_roles' field."); return; } $field = new Field(); $field->type = $this->modules->get("FieldtypePage"); $field->name = 'view_roles'; $field->label = 'Roles that can view this page'; $field->description = "Check the roles that may view this page. At least one role must be checked (like one of your roles or superuser), " . "otherwise this page does not define access. When access is not defined here, it is inherited from the template " . "or parent(s). If this page's template allows 'view' access to the user, then it will check the parent(s). " . "Access will inherit from parents that also have a custom page roles field. "; $field->derefAsPage = 0; $field->parent_id = $this->config->rolesPageID; $field->labelFieldName = 'name'; $field->inputfield = 'InputfieldCheckboxes'; $field->save(); $this->message("Added fields 'view_roles'. Add this field to any templates where you want to control view access."); $field = new Field(); $field->type = $this->modules->get("FieldtypePage"); $field->name = 'edit_roles'; $field->label = 'Roles that can edit this page'; $field->description = "Check the roles that may edit this page. At least one role must be checked (like one of your roles or superuser), " . "otherwise this page does not define access. When access is not defined here, it is inherited from the template " . "or parent(s). If this page's template allows 'view' access to the user, then it will check the parent(s). " . "Access will inherit from parents that also have a custom page roles field. "; $field->derefAsPage = 0; $field->parent_id = $this->config->rolesPageID; $field->labelFieldName = 'name'; $field->inputfield = 'InputfieldCheckboxes'; $field->findPagesCode = ' $editRoles = $page->getAccessTemplate()->editRoles; $editRolePages = new PageArray; foreach($editRoles as $roleId) { $editRolePages->add($pages->get($roleId)); } return $editRolePages; '; $field->save(); $this->message("Added fields 'edit_roles'. Add this field to any templates where you want to control edit access."); } /** * Uninstall the module * */ public function ___uninstall() { $this->message("To complete uninstall, remove the 'view_roles' and 'edit_roles' field from all templates and then delete the fields."); } } I had to keep the addable hook, otherwise it would show the "add" button on pagelist on places where user isn't actually allowed to add pages (allowed by template, but restricted be page). This same problem is with "clone" and not sure how I can get rid of it, since that comes from additional module by hooking...? Other than that my quick tests have worked perfectly. Any ideas of optimizations or further ideas? I will soon push this to github and modules directory. Also I need to write good docs for this.
    2 points
  4. You sure are fast. And it was expected you came up with other similar optimizations as well. Changes are looking good and performance gain is what I hoped for: using the dev branch cut the time on that single query from about 3 seconds down to less than 20 milliseconds. And the resulting data is the same. +1 for this! As template_id is a native field in pages, I'm sure that would perform much better just because of that. In addition that change would boost this kind of queries even more as MySQL would be able to drop more rows based on where conditions. So go for it. Actually all of this has revealed a performance issue with "has_parent!=n". That still performs poorly when there are lots of pages, especially in a deep structure resulting in lots of rows in pages_parents and thus large join between pages and pages_parents. Luckily that particular type of selector isn't one of the most used. Still possibly worth thinking if there was something that could be done. (Edit: also without the negation - I'll have to look into this more!) This is what we suspected ourselves at first also as the query in question had several fields using "%=". But it turned out that dropping all of those from the query didn't help at all. And those tests I mentioned above had these "LIKE"-operators in place again, so no problem there. I guess we need more data to hit performance issues with "LIKE". Yes, this operator thing popped into my mind also when driving home from work. Didn't see that coming at first, good thing you did. By the way, testing these kind of things needs MySQL query cache to be turned off as it screws up results when same exact queries are repeated. I did a little addition like this to wire/core/DatabaseQuerySelect.php during my tests: $ git diff diff --git a/wire/core/DatabaseQuerySelect.php b/wire/core/DatabaseQuerySelect.php index b9cbcee..4f82d2d 100644 --- a/wire/core/DatabaseQuerySelect.php +++ b/wire/core/DatabaseQuerySelect.php @@ -98,6 +98,8 @@ class DatabaseQuerySelect extends DatabaseQuery { } if(!$sql) $sql = "SELECT "; + $sql .= "SQL_NO_CACHE "; + foreach($select as $s) $sql .= "$s,"; $sql = rtrim($sql, ",") . " "; return $sql; This doesn't catch every single query but vast majority of them. I was wondering if a config variable could be added so that this SQL_NO_CACHE addition could be controlled from site/config.php? It was also helpful to add "Queries"-part of wire/templates-admin/debug.inc to the very template that had the problem. Maybe a little module should be written to display that debug data on every page when debug flag is on. Actually there are other things I'd also like to see in the bottom of every page when developing things - so yet another entry on my growing modules-to-do-list.
    2 points
  5. May someone else finds this helpful. I wanted to create comments (using built-in comments fieldtype module) via API, as I needed this is for an easy migration from an existing site. I figured following does the job: <?php // get the page that you want to add comment(s), contains a "comment" fieldtype $p = $wire->pages->get("/guestbook/"); // create new Comment object $c = new Comment(); // fill the object with values $c->text = "Hello World!"; $c->cite = "John Average"; $c->created = time(); // timestamp, if you got to migrate a existing datetime you can convert using strtotime("2011-04-09 15:14:51") $c->email = "john@average.com"; $c->ip = "..."; // not needed (only for akismet) $c->user_agent = "..."; // not needed (only for akismet) $c->created_users_id = "..."; // not needed, automaticly set by pw // set status (Spam = -2; Pending = 0; Approved = 1; ) $c->status = 1; // add new comment object to the page "mycomments" field $p->mycomments->add = $c; // save page $p->save(); ?>
    1 point
  6. This module creates a page in the ProcessWire admin where you can test selectors and browse page data and properties without editing a template file or a bootstrapped script. Given selector string is used as is to find pages - only limit is added, if given. Errors are catched and displayed so anything can be tested. Pages found with a valid selector are listed (id, title) with links to the page. I was thinking this would be useful for someone new to ProcessWire, but it turns out I'm using it myself all the time. Maybe someone else finds it useful as well. Module can be downloaded here: https://github.com/n...essSelectorTest Modules directory: http://modules.processwire.com/modules/process-selector-test/ Features Edit selector string and display results (and possible errors as reported by ProcessWire) Explore properties and data of matching pages in a tree viewLanguage aware: multi-language and language-alternate fields supported Repeater fields and values Images and their variations on disk More data is loaded on-demand as the tree is traversed deeper Quick links to edit/view pages, edit templates and run new selectors (select pages with the same template or children of a page) Page statuses visualized like in default admin theme Add pagination Screenshots
    1 point
  7. I am gonna build simple module that will add role filters and maybe a user search to help manage hundreds and more users through PW-admin. My first idea was to extend current ProcessUser module to create a new process, but stumbled on the very first step. This is my module (or part of it, but that is the part that is causing problems): <?php class ProcessUserExtended extends ProcessUser { static public function getModuleInfo() { return array( 'title' => __('Users extended', __FILE__), // getModuleInfo title 'version' => 100, 'summary' => __('Extended view for user management', __FILE__), // getModuleInfo summary 'permanent' => false, 'permission' => 'user-admin', ); } public function ___execute() { $out = ''; foreach(wire('roles') as $role) { $out .= $role->name . " "; } return $out . $this->renderList(); } } And running it gives me this error: Method Pages::getTemplate does not exist or is not callable in this context I have other options also (like hooking), but I would like to understand why this happens?
    1 point
  8. https://github.com/apeisa/CustomPageRoles/blob/master/CustomPageRoles.module It will soon be on the modules directory also, but want to write some readme and test it a little more. Yeah, that is true. What would be supernice is that the module itself would have ready translations for few languages (well, at least for Finnish). But to make that rock I would need to know how each language is called. And since we don't require strict naming conventions (which is good, we are already using different customers as languages on our help site) I don't have any way to know. This is very minor thing, but it is boring to do translation each time this module is installed.
    1 point
  9. Good ideas nik! I've added a new $config->dbCache variable. When set to boolean false (in your /site/config.php file) it adds the SQL_NO_CACHE in exactly the same way you posted. This will appear on the dev branch this week.
    1 point
  10. The default precision value is 0. I can update this to assume no precision value until it is specifically set ... I think this makes sense--thanks. The precision field is for rounding, not formatting. I think what you want is number_format(). You could do this in your output: $value = number_format($page->float_value, 2); I could add this formatting in as a configuration option with FieldtypeFloat too, but once formatted, the value is a string, not a float. And I'm not sure it's wise for us to have this Fieldtype returning strings rather than floats. There are also localization considerations with number formatting (decimal type and thousands separator), which are also the job of number_format.
    1 point
  11. Looks like ProcessPageType derived modules are a bit special. In ProcessPageType::init() $this->pages is initialized to whatever is found from the fuel with the page name. Page name being 'users' or 'roles' or 'permissions' works nicely as those keys exist in the fuel. There is a fallback to plain Pages object on the next line, but that works only so far, as you've noted. ProcessPageType::renderList() doesn't check if $this->pages is of type PagesType as is done in init(), but just tries to call getTemplate (which is defined in PagesType class). And as plain Pages object isn't of PagesType, this fails. But even though I found what happens, I'm not sure why is it so. Ryan may be the only one to say whether it's something to be fixed in ProcessPageType::renderList() for instance or is this just The Wrong Way To Go in the first place. I'd say there's just a little fix needed in the beginning of ProcessPageType::renderList() but it could be something else as well. If you want to take this road before someone's able to give you more definitive answers, you can proceed by renaming your page to 'users' (obviously not under access then, but setup for example) - that'll do the trick. And I mean *name*, title doesn't matter.
    1 point
  12. Thanks alan for mention. Yes I think if you use a custom content.css and don't have the PW classes .align_left etc in it won't show the floating in the editor. PW has those classes in the content.css from the InputfieldTinyMCE by default, so if you create you own content.css make sure you copy them over. If you want to be able to select those classes also manually via bramus you write "img.align_left{}" with the tag "img", if you don't want them to show up in the bramus style dropdown you can just write ".align_left{}"
    1 point
  13. 1 point
  14. We're probably only a few days from bringing in the new jQuery into the master branch. So far I've only come across the one issue I mentioned before and nothing else has turned up yet. If that's the only issue, I'd consider that fairly minor (easy to fix). So no reason to delay further on bringing in newer versions of jQuery and jQuery UI.
    1 point
  15. Nik, this has been pushed to the dev branch: https://github.com/ryancramerdesign/ProcessWire/commit/eecd862a6ecf90f2c6b1c173c4d129c5152a31e7 In addition adding in the isGuest() check and the has_parent check, I changed the line you added to be this: // optimization: if parent, parent_id, template, or templates_id is given, and an equals '=' operator is used, // there's no need to explicitly exclude repeaters since the parent and/or template is specific if(in_array($name, array('parent', 'parent_id', 'template', 'templates_id')) && $selector->operator == '=') $includeAll = true; Basically added in 'template' and 'templates_id' since those are also specific enough so as to warrant exclusion of the filter. And added an extra check that the operator was "=", since if they used something like not equals (!=), or greater than/less than (>< <= >=), that would still warrant inclusion of the filter. But just in case I got any of this wrong, I've kept it in the dev branch for further testing.
    1 point
  16. Ok, one more Just used this to build a rather complex expression http://gskinner.com/RegExr/ Easy peasy!
    1 point
  17. apeisa I've not tried that, but one thing I got working yesterday was alerting for non-technical editors if they mistakenly have the STRONG tag with an H tag. Of course an editor (with appropriate CSS) might want to use STRONG in an H tag so I allowed for that: h1:not(.BoldAllowed) strong:after, h2:not(.BoldAllowed) strong:after, h3:not(.BoldAllowed) strong:after, h4:not(.BoldAllowed) strong:after, h5:not(.BoldAllowed) strong:after, h6:not(.BoldAllowed) strong:after { content: "[info] (H)eadings should not be (B)old."; background: #ffffcc; font-weight: normal; border: 1px dashed red; padding: 0.2em; font-size: 12px; } h1.BoldAllowed, h2.BoldAllowed, h3.BoldAllowed, h4.BoldAllowed, h5.BoldAllowed, h6.BoldAllowed { font-weight: normal; background: #ddffdd; } The result is on-the-fly warning as the editor is editing copy in the TinyMCE textbox, fantastic. And this is only one thing I saw I could do now I have Soma-powered CSS control over TinyMCE. I'll try and add a screen-grab here of how this looks but if I don't I'm sure you see what this would look like to the user, simple and instant 'auto-help'.
    1 point
  18. I'm going to be adding an option to store sessions in the DB soon too, so this will be another alternative option for those that want it.
    1 point
×
×
  • Create New...