Jump to content

Public pages in the backend?


bernhard
 Share

Recommended Posts

Hi everybody,

just came up with an idea of a new concept how we could develop our processwire pages... As some of you might know I've developed this module to skin your backend fast and easily:

 

Why? Because several things are a lot quicker and easier to achieve in the backend then rebuilding such admin-tasks in the frontend: User login, profile editing, data presentation with access control. For example, right now I'm building a kind of dashboard for a client where users can book slots for therapies, manage their profile and data etc. This is all done in the backend and users get access after registration. So far, so good...

I really like this concept, because modules are clean and especially reusable. There is a standard for how we develop things in the backend. Everything works the same, on every project, on every installation, from any developer. Today I thought: Wouldn't it be great to have those benefits also for several FRONTEND things? Or, in other words, for several parts that need to be available for PUBLIC users (guests)?

For example, we could build a shopping cart checkout easily with the tools we already have in the backend. There are several hosted shopping-cart services where you get a hostet checkout-page that does also look a little different than the website's frontend, so I think this would not be a problem for certain tasks (https://foxy-demo.foxycart.com/checkout) The only problem is, that the user would have to register before it could use those tools. So I thought: Is there a possibility to make some areas of the backend available to the public? Is this a good idea at all? Or are we opening security holes there? Isn't FormBuilder doing something similar with the iframe-embed method?

This is just an early stage idea, but I'd be happy to hear your thoughts on this. As much as I like ProcessWire, I don't like that I have to build lots of things for the frontend over and over again...

 

  • Like 2
Link to comment
Share on other sites

I personally never would redirect visitor-users to the backend. But I use user roles and access rights a lot. So, I thought there is already a FE login / logout module available? Otherwise I think it is not reinventing the wheel everytime, as you may build custom modules or reusable template / field systems.

 

Link to comment
Share on other sites

Thx Adrian but I don't like this approach and it is not what I'm talking about. In your example the user must be logged into the admin already. If my user was logged in I would not have a problem. I want to have him use several areas of the admin without being logged in.

Link to comment
Share on other sites

I guess I am talking about the approach that module uses of loading up an admin page in an iframe. Perhaps you could adapt it to your needs to allow access to certain parts as a guest user. Not sure - sorry if I am completely off-base with what you are looking to do.

Link to comment
Share on other sites

6 hours ago, bernhard said:

Isn't FormBuilder doing something similar with the iframe-embed method?

I would rather explore the other ways FormBuilder can be used, i.e. without iframes.

In a nutshell, PW probably somehow loads all the necessary GUI assets (JS/CSS) from wire/modules/ so you can use them in your frontend.

Theoretically, you could create a new role and a corresponding user having that role, and "silently" log in each new visitor as that user, giving him all the necessary backend rights for the task at hand. But the whole concept you're envisioning is quite blurry to me, to be honest. What happens to "real" logged-in users? What data (pages?) are they actually editing/manipulating? Where is this data stored, and how do you keep track of "which user actually edited recordset x?"

Perhaps you should rather think about low-entry-barrier ways to use your web-app, such as SSO - i.e. don't discard a login entirely, but make sure that login is as painless as possible?

  • Like 2
Link to comment
Share on other sites

On 1/17/2019 at 12:41 PM, bernhard said:

Isn't FormBuilder doing something similar with the iframe-embed method?

Yes and no: the page those forms are rendered from isn't actually in the admin area. By default it's a regular page at /form-builder/. This page has a template file of its own, and when you access said page, it figures out the correct form and renders it – making use of some stuff bundled with the core for rendering the form and form fields.

Technically you can do that with any module that produces output: just figure out when and where to render it, call a method that returns output, make sure that necessary styles and scripts are loaded, and output the... err, output. If you need to do something based on user actions, implement necessary endpoints with hooks, URL segments, or based on POST/GET data.

I'm using something similar in ProcessChangelog for rendering the RSS feed generated by a Process module for front-end users: https://github.com/teppokoivula/ProcessChangelog/blob/master/ProcessChangelogRSS.module#L97:L103.

... but that's a really, really simplified version of it, and perhaps not quite what you're looking for yet. ?

On 1/17/2019 at 6:10 PM, adrian said:

I guess I am talking about the approach that module uses of loading up an admin page in an iframe. Perhaps you could adapt it to your needs to allow access to certain parts as a guest user. Not sure - sorry if I am completely off-base with what you are looking to do.

AdminBar makes use of the ?modal=1 parameter that Admin supports, in which case header/footer areas are left out of the response. I've used this method for injecting things like profile edit screens into the front-end for registered and logged-in users – though they tend to look a bit off in an iframe, so usually I use an actual modal instead – but it doesn't yet solve the permission issue.

ProcessWire's permission system is relatively flexible, but as @bernhard already pointed out, there are always risks involved when you tweak such things ?

On 1/17/2019 at 6:16 PM, kongondo said:

No authentication whatsoever? Surely you don't mean 'free-for -all' editing? I'm sure I'm missing something. Please explain.

Not trying to speak for Bernhard, but I believe the idea was to utilise the module architecture, probably Process modules in particular, to easily create front-end-oriented tools, so not really free-for-all editing per se. A booking system might be a good example – you may want to give guests a limited access to create new content or perhaps even edit/delete content based on specific set of rules, but this could also be useful for creating non-editable views.

Front-end listers, anyone? ?

---

I think that overall this idea has some potential. Not sure to what extent, or how feasible this would be, but it might be worthwhile creating a small proof-of-concept and trying things out. The permission thing is still a huge question mark, though.

If I was to build something like this, I'd probably start by looking what ProcessController core class is doing, and how it's used by the Admin itself. Though correct me if I got the whole concept wrong, and this is actually about something different entirely. There may be other ways to use the module system in the front-end as well, but Process modules are basically what the Admin side is made of, so when you say "public backend pages" – well, that's what it sounds like to me ?

  • Like 5
Link to comment
Share on other sites

13 hours ago, teppo said:

Not trying to speak for Bernhard, but I believe the idea was to utilise the module architecture, probably Process modules in particular, to easily create front-end-oriented tools, so not really free-for-all editing per se. A booking system might be a good example – you may want to give guests a limited access to create new content or perhaps even edit/delete content based on specific set of rules, but this could also be useful for creating non-editable views

That's exactly what I'm talking about ?

Link to comment
Share on other sites

On 1/17/2019 at 5:16 PM, kongondo said:

No authentication whatsoever? Surely you don't mean 'free-for -all' editing? I'm sure I'm missing something. Please explain.

OK, I'm back in the office and can share some more thoughts. Thx for your input so far.

I'm talking about "no authentication whatsoever", yes. But I'm not talking about "free-for-all-editing". Of course I don't want to have public access to all my pages, that would be nonsense. But I was wondering if it could make sense to have some tools of the backend available to the public. ProcessModules are one example. Forms could be another. I'll explain the idea by examples:

Shopping Cart

Have you ever developed a shopping cart for one of your clients? It's not that easy, right? You have to take care of data storage (cookies or localstorage or session), you have to take care of the user interface (adding items, removing them, changing amounts etc), you have to make sure everything is safe (sanitization, csrf etc) and so on. This is a lot of work to do and we all know that our website's frontends are all different, so building this for one client would mean that you have to build a lot of this functionality over and over again for the next client.

The alternate approach: What if we could build backend modules (process modules) and grant access to guest users? We could build a shopping cart module, that handles all these complicated things for us and uses built in tools, such as csrf, inputfields, routing ( executeFoo(), executeBar() ). It could have expiration features based on the session time. It could make it easy to register new user accounts. It could be 

And the best: It would be reusable! That's a huge benefit and could make developing several tools so much more efficient and fun (and maintainable).

Also think of forms in general. They are always a pain, because they need field validation (front + backend), need to remember their state on failure etc.; This is all already there in the backend! It's just hidden behind a login and I wondered if it couldn't be a huge possibility to bring certain parts of those great featureset to the public. Similar to FormBuilder, as I already stated.

Another example: Think of a kind of photo-book service. Users could visit your site, start building their photobook, upload pictures etc.; Just to try things out. It would be great to give those features to your users without any signup process and create the account afterwards. Eg. during checkout.; Or what about a theatre booking service? Display a seatmap, book tickets, show prices, etc: All a LOT to do in the frontend. If we developed a backend-oriented module where we can change colors of links, masthead etc, this would make things so much easier and it would be one click for the next project!! I hope you get my point.

Until now, I didn't really think of developing such things in the backend, because I thought it would be a lot more effort to style the backend to my needs than building a new custom frontend solution. But that's totally wrong! It's now really just changing some color codes in LESS when using RockSkinUikit module.

These pages could even be loaded in an iframe in the frontend. The height of this iframe could be adjusted via javascript, so the user wouldn't even see that he is actually in the backend. Tools are already there: It's just displaying the admin page with ?modal=1 GET parameter. While thinking of this approach new ideas and possibilities keep popping up in my head:

We could build a forum software. It could be a set of process modules, using CKEditor, image upload etc. - but of course only EDITABLE for logged in users. But the thing is: The process-modules (read-only) part would need to be visible also for guests. Building a forum software with ProcessWire as it is (or better to say: as we use it) now, would be a pain, because every installation of PW has a different backend. Moving all parts that can (and should!) be standardized to the backend would be a logical step IMHO.

 

Well... while writing I had another idea: Maybe it would be possible to create a new user + role for such modules, like "public-backend". When a website visitor add's a product to the shopping cart, he could be automatically logged in as user "public-backend". This user could then have access to the process modules that handle all the shopping cart features. We already have the demo-mode of the backend, so I think it should be possible in general to bring non-registered users into the backend.

What do you think? Sorry, ideas and visions are quite hard to explain in another language. Did I make things clearer? ? 

  • Like 6
Link to comment
Share on other sites

Just tested my idea and it works: I created a "public" role + user and it can see the hello world module when logged in. The login process could be done automatically on the frontend. Or after a simple captcha to add one very basic layer of security.

aY01rWp.png

Assigning the "public" role to the guest user does work, but visiting the processmodule does redirect the user to the login-screen.

I'm not sure about the security of this approach, though. What do you think @Robin S @BitPoet @tpr ? For example, the PW version is displayed in the footer. This could easily be fixed, but also config data is sent to the client via the ProcessWire js config object. But on the other hand, the PW access control system should be safe as it is - so if a role had only one permission assigned for a special processmodule, it should not be a security concern, right?!

  • Like 1
Link to comment
Share on other sites

2 hours ago, bernhard said:

Did I make things clearer?

Thanks for the examples! It was already clear from @teppo's post since you said that was exactly what you meant ?. A few more examples don't hurt though, so thanks.

2 hours ago, bernhard said:

What do you think?

Hmm. Personally, I'll have to think about this more. The idea is very clear but I'll want to think about the implications first.

  • Like 1
Link to comment
Share on other sites

1 hour ago, bernhard said:

What do you think @Robin S

Speaking personally, I wouldn't want to reveal the admin URL to visitors on the frontend.

I haven't thought about the practicality but if I wanted to render admin form elements and Process module output on the frontend I think my approach would be to render it via frontend templates and include whatever admin CSS or JS was needed. So basically the approach taken by FormBuilder, and if an iframe could be used that would of course reduce the chance of CSS conflicts. Where AJAX responses were needed (e.g. PageAutocomplete fields) then some special frontend page/template would be needed for this, or perhaps the idea I floated here:

 

  • Like 1
Link to comment
Share on other sites

Thx Robin,

the hook in your link is an interesting idea to handle AJAX ? But I think including CSS + JS from the admin into the frontend would make things tedious...

Another approach would be to create a FrontendTheme similar to the AdminTheme that works the same on all installations. But still all the great backend features (file upload, inputfields etc. would have to be rebuilt and that's what I want to avoid.

  • Like 1
Link to comment
Share on other sites

15 hours ago, bernhard said:

Maybe it would be possible to create a new user + role for such modules, like "public-backend". When a website visitor add's a product to the shopping cart, he could be automatically logged in as user "public-backend".

That's more or less what I suggested (vaguely) above:

Quote

Theoretically, you could create a new role and a corresponding user having that role, and "silently" log in each new visitor as that user, giving him all the necessary backend rights for the task at hand.

 

  • Like 1
Link to comment
Share on other sites

Sorry dragan, totally missed your post! Seems your comments where exactly what I've come up yesterday ? 

On 1/17/2019 at 6:43 PM, dragan said:

But the whole concept you're envisioning is quite blurry to me, to be honest. What happens to "real" logged-in users? What data (pages?) are they actually editing/manipulating? Where is this data stored, and how do you keep track of "which user actually edited recordset x?"

Yeah - it's an early stage idea ? Real logged in users could just have more rights than silently logged in users.

What pages are created would have to be managed by the module. But that would be the same if you built that app in the frontend! Take again the shopping cart example: What if you wanted to provide an image upload feature for your user? You'd need to create a temporary page and make sure that the user can only edit this page. If the user did not finish checkout, you'd delete this page after 1 day. The same thing could be done in the backend.

I think it is not always necessary to keep track of which user edited recordset x. Again the shopping cart: It would be enough to count how often a product was added to a cart by a public user, or how often a cart was checked out successfully. But as long as the user does not check out the cart, it might not be necessary to log him in as a real, unique user.

Another example: What about a public guestbook? Users could click on "create new entry", then the backend could create an unpublished page that only this user can edit (don't know yet how this could be done), the user could save this page (having all backend features like url field sanitization, email, ckeditor input, file upload) and an admin could then publish it after approval. Or think of a forum software: Logged in users could create new content, but public users could only read entries. This would also make a lot of sense to be built in the pw backend as we already have a standard there: Uikit, ProcessModules, jQuery. And boom, one Forum Module could then be instantly used on every other PW installation, with easy theming support via LESS (eg RockSkinUikit).

We could even work a little bit on the admin to make it easy to replace the header + footer of the theme, so it would be even easier to integrate the whole forum into an existing website by just injecting the menu and footer section!

Link to comment
Share on other sites

27 minutes ago, bernhard said:

If the user did not finish checkout, you'd delete this page after 1 day.

From a usability POV, I'd actually try to remember every visitor's cart. You never know if they come back and want to finalize that purchase. It would also be good business sense I guess. You could use cookies or local storage or whatnot (it ain't rocket science).

Perhaps one major tool in your toolset would be SSO. Why has it become so popular? Exactly for such scenarios that you mention (and much much more). Start using a service quickly without the hassle of registering (and then waiting for that confirmation mail...), right away. And remember my choices that way.

Link to comment
Share on other sites

6 minutes ago, dragan said:

From a usability POV, I'd actually try to remember every visitor's cart. You never know if they come back and want to finalize that purchase. It would also be good business sense I guess. You could use cookies or local storage or whatnot (it ain't rocket science).

Sure, it was just an example. This would of course be possible.

6 minutes ago, dragan said:

Perhaps one major tool in your toolset would be SSO. Why has it become so popular? Exactly for such scenarios that you mention (and much much more). Start using a service quickly without the hassle of registering (and then waiting for that confirmation mail...), right away. And remember my choices that way.

A shopping cart only functional after signing in? No guest-checkout? No, that's not what I'm thinking of ? But yes, SSO could be a good tool for many other usecases using this approach!

Link to comment
Share on other sites

  • 4 weeks later...

Hey guys,

I played around with this idea a little more, because I have 3 projects where I need similar functionality and I really don't want to build all that functionality over and over again for every single project (and all future ones).

I created a short screencast that shows how it works. The concept is to hook into ProcessLogin::execute, because this method is what every guest user will see regardless of what admin page he requested:


$wire->addHookAfter("ProcessLogin::execute", function(HookEvent $e) {

  $requestedPage = $this->session->getFor('ProcessPageView', 'loginRequestPageID');
  $process = $this->pages->get($requestedPage)->process;
  $segment = $this->input->urlSegment1;
  $method = 'execute' . ucfirst($segment);

  // debug info
  if(function_exists('bd')) {
    bd($requestedPage, 'requestedPage');
    bd($process, 'process');
    bd($segment, 'url segment');
    bd($method, 'method');
  }

  // prevent infinite loop
  if($process == 'ProcessLogin') return;

  // prevent warning on loginpage without /login urlsegment
  if($process == 'ProcessPageList') return;

  try {
    $e->return = $this->modules->get($process)->{$method}();
  } catch (\Exception $e) {
    $this->error($e->getMessage());
  }
});

ProcessGuest.module is a simple module that shows that this concept actually works pretty well:

<?php namespace ProcessWire;
class ProcessGuest extends Process {
  
  public static function getModuleInfo() {
    return array(
      'title' => 'GuestAdmin',
      'summary' => 'GuestAdmin to ProcessWire',
      'version' => 106,
      'permission' => 'page-view',
      'page' => [
        'name' => 'guest',
        'title' => 'Public Guest Page',
      ],
    );
  }
  
  public function execute() {
    $this->headline('execute()');
    $this->wire('processBrowserTitle', 'execute()');

    $form = $this->modules->get('InputfieldForm');
    
    // create fieldset
    $fs = $this->modules->get('InputfieldFieldset');
    $fs->label = 'This is a public backend page demo';

    // add status inputfield
    $f = $this->fields->get('title')->getInputfield(new NullPage());
    $f->label = 'foo';
    $f->columnWidth = 30;
    $fs->add($f);

    // add button field
      $b = $this->modules->get('InputfieldButton');
      $b->value = 'Demo button';
      $b->icon = 'bolt';

      $f = $this->modules->get('InputfieldMarkup');
      $f->columnWidth = 70;
      $f->value = 'Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Duis leo. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Sed aliquam ultrices mauris. Vivamus euismod mauris.<br><br>';
      $f->value .= $b->render();
      $fs->add($f);

    $form->add($fs);

    $form->add([
      'submit' => [
        'type' => 'submit',
        'value' => 'submit',
      ],
    ]);

    return $form->render();
  }

  public function executeFoo() {
    $this->wire('processBrowserTitle', 'executeFoo()');
    $this->config->scripts->add('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.js');

    return 'foo <div id="moment"></div><script>$("#moment").text(moment().format());</script>';
  }

}

See it in action (and notice that the user is NOT logged in):

Of course, this is a dirty hack, but it seems to work quite well and it might be good enough for a first proof of concept implementation to get ryans attention about it. What do you think?

Unsolved problems so far where I could neet your input:

  • Everything is centered - where does this styling come from? I know that the login-form get's centered, but I didn't find the exact class/script that is responsible for it so I couldn't remove it.
  • Any ideas how we could use a similar technique but with a custom admin url so that we could mask the original admin url of the backend? Maybe hooking into a 404 exception? Or creating a custom frontend page? But how could one render an admin page then?
  • $this->headline() does not work, so there might be other things not working with this technique...
  • Like 4
Link to comment
Share on other sites

Hi @bernhard,

I did some experimentation with an alternative idea for viewing Process modules on the frontend. I put together a proof-of-concept module you might like to check out:

https://github.com/Toutouwai/ProcessFrontendAdminPage

Process Frontend Admin Page

A demonstration of how a Process module could execute on the frontend. This is just a proof-of-concept.

frontend

Usage

Install the ProcessFrontendAdminPage module.

Create a new role named "frontend-only". Give this role the "process-frontend-admin-page" permission and no other permissions.

Create a new user named "frontend-only" and give them the "frontend-only" role.

View the "Demo frontend admin page" on the frontend while not logged into the PW admin.

How the module works

On install the module creates a "Demo frontend admin page" under Home. This page uses the admin template with the ProcessFrontendAdminPage process assigned.

When this page is viewed by a guest they are silently logged as the "frontend-only" user. This allows them to see the rendered output of the Process module.

The header and footer of the admin theme are not rendered when the page is viewed.

When a user logged into the "frontend-only" account accesses any other page they are logged out. This is for the sake of logged-out users of other roles, so that when they go to log into the PW admin they don't find themselves already logged in as the "frontend-only" user.

Taking the concept further

You could embed the ProcessFrontendAdminPage output in an iframe on another page, to preserve a global header/footer and avoid conflicts with the admin CSS.

You could add extra markup, Javascript and CSS to the ProcessFrontendAdminPage output to make it look more like the rest of your site.

You could create additional executeSomething() methods to render different output depending on URL segment.

You could create additional pages within your site that use the admin template and assign the ProcessFrontendAdminPage process. I think you would have to use API code to do this. Then in the execute() method you could check the name of the page being viewed and render different output accordingly.

  • Like 9
Link to comment
Share on other sites

Rabin, thank you for this. You have:

 // Pretend that this page is being loaded inside a modal
 // So that the admin header and footer are not rendered
 $this->wire('input')->get->modal = 1;

This might not be quite true, depending on what is understood by "rendering", as header and footer are rendered, they are just hidden via css. Of course, that's not a "problem" with the module, that's how PW admin works, but it always bothered me.

I also never saw anything like this:

$page = $ppv->_callMethod('getPage', array());

What is this _callMethod?

Link to comment
Share on other sites

14 minutes ago, matjazp said:

This might not be quite true, depending on what is understood by "rendering", as header and footer are rendered, they are just hidden via css.

The HTML for these elements is not rendered at all - in AdminThemeUikit at least. See $layout == 'modal' and $adminTheme->isModal here.

 

18 minutes ago, matjazp said:

What is this _callMethod?

It's a way of calling protected class methods from hooks. See here.

  • Like 3
Link to comment
Share on other sites

Thx Robin, this is (almost) awesome! ? 

I forked your repo and did two minor changes: 

  1. I implemented a hookable method to modify the HTML, here to remove the admin url that is exposed in the pw config js object: https://github.com/BernhardBaumrock/ProcessFrontendAdminPage/commit/1fed071d3f951b82fbae6c98b2c4834efec2b6bd
  2. I removed the $session->redirect in the processmodule. I guess the reason why you did that was to get rid of the ?modal=1 parameter in the url? The parameter is appended in InputfieldForm when ?modal=1, so I reset this parameter on initialisation of the process: https://github.com/BernhardBaumrock/ProcessFrontendAdminPage/commit/d19f71c03b7169734dca243bc45f35a205782bf3

Do you know how/if we could throw a regular 404 on a Unrecognized path exception in the backend?

OvEB0Cx.png

Thank you very much for your work on this! ? 

  • Like 3
Link to comment
Share on other sites

This looks really promising! What do you think?

PhA5Hgi.gif

I'm using this library: https://github.com/davidjbradshaw/iframe-resizer

It's as simple as adding your iframe to your frontend:

<style>iframe{width: 1px;min-width: 100%;}</style>
<iframe id="myIframe" src="/your-public-backend-page/" scrolling="no"></iframe>
<script>iFrameResize({
  log:true,
  bodyMargin: '0 0 30px 0',
}, '#myIframe')</script>

Further improvements could be to sync the page url with the iframe url, but the great thing is: We'd only have to do this once and could use it on all our projects. No matter what frontend we are using!

Imagine what a powerful tool this could be: Creating forms, registration pages, carts, event signup pages etc etc., everything as modular and reusable as we know it from the pw backend. And everything instantly styleable via RockSkinUikit ? 

  • Like 3
Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...