Jump to content

apeisa

Moderators
  • Posts

    4,632
  • Joined

  • Last visited

  • Days Won

    55

Everything posted by apeisa

  1. I thought about text translations, but not sure how to implement these. Since only strings that should be translatable are those edit_roles and view_roles field labels and descriptions. And since they are used in install method, they will go right into default language values. So for me it is not clear how to make this translatable? Or then I am just missing the obvious...? About hard coded role... not sure why I have used role there, it should be permission of course (so no need for settings). i'll make that fix quickly.
  2. 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.
  3. Ryan, that collapsedHidden did the job, thanks! About setting template permissions first and then filtering those per page basis.. it works perfectly with view permission, where we want lot's of roles first see, end then restrict on deeper levels. But edit permissions usually go other way: home page has less editors and then as we go deeper sections then we add more editors. Not sure I understand. If I have restricted access on page (template: "team-front") level to be editable only for "boys-03" and it's children (and their children and so on) then don't define access. I do need to loop through parents to find that restrictions - if I just fall back to template access, then all roles that can edit team-front can edit also those children. But if I do keep my parents loop then it should work. While I think it wouldn't usually work from home template (I cannot allow all with edit access there), I think it would work just fine on "team-front" template and others like that. So I will definitely try that out next. I also like that when build on that way, then editors doesn't see all the possible roles when choosing who can edit, but only the ones that site builder has defined could be editors there.
  4. Thanks Ryan. This might work, but I need to think about it little bit more. Do you mean that if I have template, say "team-front" and then 20 roles like "boys-02", "boys-03" etc.. I would then edit team-front template and let all those roles to edit and create pages with that template. After that I would be able to select those 20 roles from page-edit view? It wouldn't be as "ad-hoc" as i would like to be, but also gives even more granular control. I think I look into this approach next! I think that is what I did first. At least when checkboxes are used it cleared all selections when those checkboxes were missing. That is why I ended with that simple hiding trick.
  5. Other options for quick and dirty: iFrame JS-widget (ok, can be pretty time consuming) Joomla might have some kind of proxy plugin (which looks for external url and grabs contents there) But it seems that your thinking is just fine. If you install PW to same domain, then you can use Ryan's ServicePages module and get JSON from there: https://github.com/ryancramerdesign/ServicePages
  6. I have been developing this module little further. There are some nasty problems where I could need some help. This is my current code: <?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("Page::deleteable", $this, 'deleteable'); $this->addHookAfter("Page::sortable", $this, 'sortable'); //$this->addHookAfter("Pages::find", $this, 'hookPagesFind'); $this->addHookAfter("InputfieldCheckboxes::render", $this, 'hideRoleCheckboxes'); //$this->addHookBefore("ProcessPageEdit::buildFormRoles", $this, 'hideFormRoles'); } public function hideFormRoles(HookEvent $event) { $event->replace = true; $event->return = new InputfieldWrapper(); } /** * This toggles the edit_roles and view_roles for a text list for regular users (not superuser or client's master role) * */ public function hideRoleCheckboxes(HookEvent $event) { $user = $this->user; $page = $this->page; if ($user->hasRole("paakayttaja") || $user->isSuperuser()) return; // This is pretty ugly hack to just hide visually those UA fields if ($event->object->name == "edit_roles" || $event->object->name == "view_roles") { $event->return = "<div style='display:none;'>{$event->return}</div>"; } } 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; } } public function deleteable(HookEvent $event) { $page = $event->object; if ($page->editable()) { $event->return = true; } else { $event->return = false; } } public function sortable(HookEvent $event) { $page = $event->object; if ($page->editable()) { $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->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."); } } It has one hard coded attribute (role name "paakayttaja" ["superuser for client"], which will of course be a setting later on). This code works fine, you can choose which templates can have page based user access. This allows to choose edit and view roles (superuser and "paakayttaja") can make the selections. But when using this module users who get their edit access through page permissions cannot create new pages, but get this error message: This can be avoided by adding hook into ProcessPageAdd::isAllowedTemplate and putting $event->return = true; there. This is my first problem: 1) How should I handle this situation? I do want to allow people to create a new pages, but not just any pages - they cannot choose any template (ie add "home" page somewhere). So we definitely need template restrictions. One way to think of it is that we always need to have restricted bunch of templates allowed in family tab. Even if I have only one template allowed, I get the error. Would it be good solution to look for allowed templates for children, and then return true for those templates? That would also require the ProcesssPageAdd::isAllowedTemplate hook. Not really sure what is best way to go here - any ideas? 2) This is much simpler one. I want to have those view_roles and edit_roles be editable only for "paakayttaja" role. How can this be achieved now? Currently I have ugly hack to just hide the selections (see hideRoleCheckboxes method). This also falls apart if one changes the inputfield for edit_roles or view_roles field. Other than that: when we get this working I think we have pretty much perfect UA in my opinion. Simple and powerful template based UA + page based permissions only there where they are needed (ie. when you create site for football team that has 15 junior teams and they all need similar area on the site).
  7. Thanks Nik, well described. I'm on mobile, so little spare on words. Gazley: you mean on pw admin? My templates are just normal. If I have template called "news-item" I'll let it go to news-item.php. Many times actual template file is very short, sometimes only the included index.php file and chosen layout (if not default).
  8. Layout could be user defined field. I haven't got the need yet. Layout is more like inner template. All pages have same header and footer, these come from index.php. But then in main section of the site I usually have few different layouts, one for frontpage, two columns, three columns etc. Then if my template doesn't use default one, then I just switch it in template file: $page->layout = "wide"; That setting could easily be user defined if you want client to choose from different content layouts. As you can see, I haven't come up with good term for that inner template. Since pw uses template, I ended up calling it layout. And I set that $page->layout = "wide" in my template code.
  9. I am having problems with this module. It seems to save the embed codes into the database, which is good. Bad thing is that it saves only one line there. This causes every video to be the same. It seems to fail on video_id part, since my only row in textformatter_video_embed table is this: www. <iframe width="640" height="360" src="https://www.youtube.com/embed/9StIWIg03eM?fs=1&feature=oembed" frameborder="0" allowfullscreen></iframe> 2012-09-19 10:40:44 So it saves only www. as video_id and seems to that with every video. Is this problem only on my installation or are other experiencing the same? My url in textarea is in this format: https://www.youtube.com/watch?v=9StIWIg03eM&feature=related
  10. Damn you are pro soma, thanks! Will test that soon.
  11. Soma, thanks for this! One thing I started wondering after implementing simple styles for p-tags is that is there a way to add things like: div.noticebox { background: #f3f3f3; border: 1px solid #eee; padding: 1em; } div.errorbox { background: white; border: 1px solid red; padding: 1em; } And have those appear as a selection in tinyMCE? So one could have noticebox and use headers and paragraphs inside it. After reading the bramus site I think that it's not possible, but thought that I ask from our TinyMCE specialist first
  12. Haha, I took it to right. After 15 minutes I thought that I might have better things to do That is pretty amazing though.
  13. Totally offtopic: about xkcd, this latest is also very interesting: http://xkcd.com/1110/
  14. It would be ubercool to have simple newsletter builder which would use Amazon SES for email delivery: http://aws.amazon.com/ses/
  15. Just did my duties, and it took 5 attempts with recaptcha. But once again human was stronger than evil computer.
  16. Have you read this already: http://processwire.com/api/multi-language-support/multi-language-fields/ If you don't need localized urls, then no need for additional module.
  17. Not totally sure yet, but I think we will list all the companies and freelancers who prove to have released PW sites/apps and are willing to take more work with PW in Finland. We don't want to duplicate on what will be on this site, so if official directory that allows for country filter is available then we probably just link there.
  18. Yeah, there definitely was some strange going on with that feed. Now it seems to be working on my end too, so they must have been fixed that. Ryan: have you thought about adding that multisource functionality to this module? I am already using it in couple of places, and it has been working great. Of course if you think the implementation should be different or alltogether different module then let me know (or if you prefer github pull request). What I was thinking it might be more "pw" to have add->(source_url) etc and then load, instead of having all the urls in array load($array_of_urls) like it is currently.
  19. We are building local "marketing site" for PW in Finland. It will introduce all the companies / freelancers who use PW and also show some of the best parts of the system. It will be for people who buy websites, not so much for the people who build websites. Hope we can find time with Teppo to finish that site soon. It would be great to have bunch of local sites. Btw, symphony has this kind of stuff coming: http://getsymphony.com/discuss/thread/91734/
  20. I don't think that tc_calendar class is even part of processwire. Do you have that external class uploaded to your server and included at some point?
  21. Ryan's php.ini guess seems to be the answer: http://www.directadmin.com/forum/showthread.php?t=43032&p=218391#post218391
  22. Two options: change it to rich text editor (edit the field and then details tab => Inputfield type => tinyMCE) or then add newlines to br textformatter (install it from modules, then edit the field and add the formatter from details tab).
  23. It creates new one.
  24. More spam: on preview tab it would be nice to have edit icon or link on each field, which would be a shortcut for field edit.
  25. Also might be required: rich text possibilities for success output. Also possibility to add another success output for email confirmation. Rich text mainly for creating links.
×
×
  • Create New...