Problems, need help? Have a tip or advice? Post it here.
14 posts Page 1 of 2
Hello,
I'm currently developing a website (on extended license), which will consist mostly of training videos grouped into courses, which the users would be able to purchase access to.
I have already configured the Extended users functionality, and arranged courses and individual trainings into Page & Folder structure.
Now my question is - do You have any proven solutions to bind the folder access to the extended user's profile parameters?
I tried to make it work by using conditionals to display the training page (a.k.a. episode from the course series) only if the user's custom field, corresponding to k_page_foldertitle of the training page, is checked.
However, I had no success implementing such mechanics.
Do you have any tips on how to make such relationship work? At this point I'm not sure whether I made some errors with coding, or my entire approach is wrong.
Thank You in advance :)
Isn't it as simple as establishing a relation from extended users template to extended folders template?
trendoman wrote: Isn't it as simple as establishing a relation from extended users template to extended folders template?

Wow, is there an addon for folders, in the same manner the users functionality can be extended? I'll have to look for some articles/instructions about it.
Thanks!

EDIT:
Ok, I think I found the post about it. So, when I set it up properly, the simplest way would be to create an editable type='relation' within the extended user's form, right?
Then, one thing, I wasn't able to determine myself - how to properly call such custom user input data for the sake of <if> condition? Should it be invoked with <pages...user_template> or <get_field> beforehand?
@theV, the site is structured as follows -
Code: Select all
Course A
    Episode 1
    Episode 2
    Episode 3
Course B
    Episode 1
    Episode 2
where a 'user' can subscribe to several 'courses'.

To create such structure, as @trendoman suggested, we can use 'extended folders' and then relate them to the users.
I, however, have a feeling that using a separate template for the 'courses' (instead of using folders) would be easier to handle.

Following this approach we can use, for example, a 'courses.php' clonable template to represent the courses and another clonable template 'episodes.php' for the episodes.

Use type='reverse_relation' in courses.php to relate it with episodes (please see viewtopic.php?f=8&t=8559#p16293 for a working example - it is for a gallery but is totally applicable for normal templates).

This way we'll have a 'one-to-many' relationship between courses and the episodes (a course can have many episodes while an episode can only belong to a single course) and the 'reverse_relation' should make it easy to view, add, manage all 'episodes' related to any course from the admin panel.

Coming to second relation (many-to-many between users and courses), we can use a relation region in the extended-users template so while editing a user we can relate her to the subscribed courses. This, of course, can be done programmatically when a user successfully pays for a course.

Finally coming to the frontend, we can use the enhanced <cms:pages> (viewtopic.php?f=5&t=8581) to query the related pages e.g.

1. In the list-view of courses, we can show only the the courses subscribed by the logged-in user.
2. In the page-view of an episode - we first query which course the episode is related to and then a second query to determine if the user is related to that course. Depending on the outcome, we can vary the view.

To make the process more manageable, you can create some reusable functions (viewtopic.php?f=8&t=11368&start=10#p30174) that do all the querying through cms:pages and simply return if the user should be shown the episode or not.

Hope this helps.
Do let us know if you need help with anything.
@KK, Access check, it seems to me, is a perfect job for a custom route filter when TS opts to build a more advanced app.
Hello again,
Thanks for Your help!
I eventually related users to the course (the other way around, the relation field is placed in 'training.php' and it links to the list of users, as I've discovered this would be a little easier to control with coding).
I have one more question - I think I miss something while coding the enhanced pages relation. Of course if I hard-coded the user name, the proper trainings show, but I cannot figure out the way to implement the k_user_id variable for the relation to find the currently logged user.

I tried following alternatives (in order for Couch to fetch only the courses the logged user is currently subscribed to):
<cms:pages masterpage='training.php' custom_field="user_access=id(k_user_id)" >

<cms:pages masterpage='training.php' custom_field="user_access=k_user_id" >

and then, tried to set up a custom variable as well:
<cms:set myname="<cms:get_field 'k_page_name' masterpage=k_user_template page_id=k_user_id />" />
<cms:pages masterpage='training.php' custom_field="user_access=myname" >


There must be some way to instruct Couch to get the correct user page name, but I must be doing sth wrong.

EDIT: Nevermind, I found out my foolish mistake ;)
<cms:pages masterpage='training.php' custom_field="user_access=id(<cms:show k_user_id />)" >

This solved the problem :)
Okay, another question about this project :)

Like You suggested, I utilized custom routes to link the training and validate user access. It works like charm for the training list view - if the user has access, they are brought to the episode list, and if they don't, the script redirects them to the preview page. The route filter is pretty standard there:

Code: Select all
<cms:pages masterpage='training.php' id=rt_id custom_field="user_access=<cms:show k_user_name />" limit='1' show_future_entries='1' >
   <cms:no_results >
      <cms:redirect url="<cms:route_link 'preview' masterpage='training.php' id=rt_id />" />
   </cms:no_results >
</cms:pages >


Now, I need similar filter for the single episode page itself, in order to prevent unauthorized access by pasting direct episode link and overriding the training list view filtering.

In order to achieve this, I would have to get the id of the training the episode in question is related to; I tried a dozen configurations with <cms:related_pages> or <cms:pages custom_field="...">, but with zero effect :/

Currently the filter coding looks like this:
Code: Select all
<cms:pages id=rt_id limit='1' show_future_entries='1' >
   <cms:set training_id = "<cms:related_pages 'parent_training'><cms:show k_page_name /></cms:related_pages >" />
</cms:pages >
<cms:pages masterpage='training.php'
   id='training_id'
   custom_field="user_access=<cms:show k_user_name />" limit='1' show_future_entries='1' >
      <cms:no_results >
         <cms:redirect k_site_link />
      </cms:no_results >
</cms:pages >

What am I doing wrong?

To explain the situation better, the relations are as follows:
Training clonable page (training.php) has many-to-many relation editable to user (user_access),
Episode (training_episode.php) many-to-one relation with the Training pages (parent_training).

Thanks in advance!
From what I see, there are three problems with the code -
1. You are trying to get the 'ID' of the related training page as follows -
Code: Select all
 <cms:set training_id = "<cms:related_pages 'parent_training'><cms:show k_page_name /></cms:related_pages >" />

However, in the code above you are capturing the page's 'name' and *not* its 'id'. You should use <cms:show k_page_id /> for that.

2. In the subsequent code where you try to further use the captured ID -
Code: Select all
<cms:pages masterpage='training.php'
   id='training_id'
   ...
In the code above you are using 'quotes' around 'training_id' - this will suggest to Couch that you are trying to find a page with *literal* ID as string 'training_id'; which, of course, is wrong. Variable names are never quoted (please see https://docs.couchcms.com/concepts/sett ... eters.html if you are unclear about this).

3. The variable 'training_id' is set within the first <cms:pages> block - this will limit its availability only within that cms:pages block (this is 'scoping'). So, even if it is correctly set, it won't be available, by default, to the second <cms:pages> block. We'll need to set the 'scope' of that variable to either 'global' or 'parent'.

I think the following amended code should do it -
Code: Select all
<cms:set training_id='' scope='global' />
<cms:pages id=rt_id limit='1' show_future_entries='1' >
   <cms:set training_id = "<cms:related_pages 'parent_training' show_future_entries='1'><cms:show k_page_id /></cms:related_pages >" scope='global' />
</cms:pages >

<cms:if training_id>
    <cms:pages masterpage='training.php'
       id=training_id
       custom_field="user_access=<cms:show k_user_name />" limit='1' show_future_entries='1' >
          <cms:no_results >
             <cms:redirect k_site_link />
          </cms:no_results >
    </cms:pages >
<cms:else />
    <cms:redirect k_site_link />
</cms:if>

I have added check for the possibility of the current episode page or its related training page not being found by explicitly setting 'training_id' to blank before hand and then checking if this properly set later.

That said, perhaps the following alternate version of the code should also work -
Code: Select all
<cms:pages id=rt_id limit='1' show_future_entries='1' >
    <cms:related_pages 'parent_training' custom_field="user_access=<cms:show k_user_name />" limit='1' show_future_entries='1'>
        <cms:no_results >
            <cms:redirect k_site_link />
        </cms:no_results >
    </cms:related_pages >

    <cms:no_results >
        <cms:redirect k_site_link />
    </cms:no_results >
</cms:pages >

Hope this helps.
The filter works alright now, thanks a lot! :)
The more I get to know Couch, the more amazed I am with its endless possibilities :) Now it's time for me to work on integration of the payment gate, and then - the website's done. Honestly, I couldn't dream of a better CMS for websites with custom solutions :)
Hi,
There is one more thing I'd like to ask about.
I made an order system based on cart and checkout module (I'm still implementing the payment gate, so now the checkout procedure skips this step). When trainings are ordered, and the order is finalized, the CMS is redirecting to a custom route which does two things via db_persist commands:
1. Edits the clonable page for the training in question, in order to add the user as the training's subscriber (thus granting them access to the training),
2. Creates a new clonable in Orders template - it stores the user info, ordered trainings (many-to-many relation), and dates.

The order date is, of course, generated automatically, using the current time of the order's creation. Now, I would need another date - which I call 'expire_date' - generated with the order as well, but setting the time automatically one year forward.
So, if today an order was created, the creation date would be 24.07.2019 and the expire date should be set to 24.07.2020.
This second date would then terminate the user's access (I imagine it could be done by the CMS reacting when the current day is later than the expire date).

What I'd like to ask You about - Is there any known solution to set dates in forward, adding one year to the current day? I couldn't find any tips on such command's syntax.

Thank You!
14 posts Page 1 of 2
cron