Forgive me if this should be posted elsewhere, its my second post ever.
I have seen over time many requests for protected client areas; essentially a way for studios to offer protected areas for their clients. I built one yesterday with help from a post on http://www.keighl.com and Mark Huot on EE forums. This is how I did it.
First I’ll go over the requirements of the system:
-It must use EE’s user system as the authentication. Meaning a client should be able to login using the default EE login form, and then access their protected page.
-It should be secure. Only logged in clients can view files, and can only view their own files/images/pages.
-It should be easy to update and add to.
Here is an overview of how my system works.
I have created a new member group on the site, called ‘Clients’. Only Super Admins can assign users to this member group.
I have created a new weblog called Clients. Custom fields for this blog can be extensive. I used Mark Huot’s file extension to allow me to upload images and files and attach them to posts. Two of my custom fields are as follows:
Project Image Previews* {project_previews} Field Type: File
Project Deliverables* {project_deliverables} Field Type: File
I have also created two new upload directories. They are ‘clients/images/ and ‘clients/files/’. Each file field uploads to its respective location.
I have created a new template group called “Clients”. The structure of the ‘clients’ template group is as follows:
-index (web page) -image (web page) -file (web page)
I went into the Template Preferences Manager and assigned preferences. Here’s what you will need to do for ALL THREE Templates. [ Restrict access to the ‘clients’ member group alone, allow PHP: input stage ]
Now to the actual templates. Index is simple, and I will take out only the necessary code to show you how it works.
<head>
<title>Logiq Design || {username} Client Area</title>
<!-- rest of your xhtml head here, including css and javascript, meta, etc. -->
</head>
<body>
<!-- All of your regular code and such goes here. -->
<div class="client_project">
<?php
global $SESS;
$logged_username = $SESS->userdata['username'];
?>
<!-- the preceding php script allows us to use the logged in users username variable inside a weblog entries tag. This is otherwise impossible without php due to parsing order, from my understanding !-->
{exp:weblog:entries weblog="clients" rdf="off" dynamic="off" url_title="<?php echo $logged_username;?>"}
<h1>{username} Project Image Previews</h1>
<!-- After we add the code to the 'image' template, the following code will output the image in its place here. If a user views the source or the image location, or tries to copy and paste it into their browser, it won't function. It will only work inside this template. The user can still save the image, but they can't get the direct path to the image. !-->
{project_previews}
{path=clients/image}{file_name}/image_bin/
{/project_previews}
<h1>Project Deliverables</h1>
<!-- the following code is similar to the one we used for the project image previews, it relies on the file field but this time the 'file' template. !-->
{project_deliverables}
<a href="http://{path=clients/file}{file_name}/file_bin/">{file_name}</a>
{/project_deliverables}
{/exp:weblog:entries}
</div> <!-- ends the 'client_project' div !-->Now the key things about this template is that when it renders the image or file, the path will be as we wrote it. So for example on my site, when I view the image and look at the source code, it says “http://www.logiqdesign.com/index.php/clients/samplepreview.png/file_bin while the actual location of the image is somewhere else.
For the image template, enter the following.
<?php
$image = "{segment_3}";
$url_path = "{segment_4}";
switch ($url_path):
case "image_bin":
$path = 'clients/images/';
break;
default:
exit();
endswitch;
if (is_file($path . $image)) {
$f = $path . $image;
} else {
exit();
}
header('Content-type: image/png');
$im = @ImageCreateFromJPEG ($f) or // Read JPEG Image
$im = @ImageCreateFromPNG ($f) or // or PNG Image
$im = @ImageCreateFromGIF ($f) or // or GIF Image
$im = false; // If image is not JPEG, PNG, or GIF
if (!$im) {
readfile ($f);
} else {
@ImagePNG($im);
}
?>IMPORTANT: On line 8 replace the text between the ’ ’ marks with the path to where you store your client images, for example clients/images/. You will not want to use the Main Upload Directory.
Now lastly for the ‘file’ template. Enter the following code.
<?php
$file = "{segment_3}";
$url_path = "{segment_4}";
switch ($url_path):
case "file_bin":
$path = 'clients/files/';
break;
default:
exit();
endswitch;
if (is_file($path . $file)) {
$f = $path . $file;
} else {
exit();
}
if (file_exists($f)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($f));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($f));
ob_clean();
flush();
readfile($f);
exit;
}
?>IMPORTANT: Replace the path in line 8 with the location of your client files.
Now all you have to do when you get a client is register them as a user. Then create an entry in the ‘clients’ weblog with the same urltitle as their username.
For added security, I recommend using .htaccess to password protect the directories you upload clients work to. Since PHP can bypass this, it won’t affect the script.
Thanks for sharing! Here is a bundle of questions 😊
Does this mean you only dedicate 1 entry to a ‘client files/project’? How do clients get to interact with this page? SAEF or CP? What happens when you have multiple projects with 1 client? Does each client get a dedicated upload location? Why use Mark’s ext. if EE2 has a file upload capability? Why use PHP if EE has native way of getting loggin user and username?
Nice thing about EE is that if offers many ways to get the same thing done I am just wondering why such a non-EE way of doing this… Thanks again!
Hey there iebisol,
Thanks for the interest and the reply.
I’ll try to answer your questions to the best of my ability. 1.) Does this mean you only dedicate 1 entry to a ‘client files/project’? Yes. In the solution I posted you would use one entry for each client / project.
2.) How do clients get to interact with this page? SAEF or CP? As I just coded this yesterday, I haven’t tweaked a lot of it yet and perfected this aspect; however one way I am currently doing it is by using displaying comments by url_title and a comment form with url_title. Clients can then post directly to that page. I’ll look at a SAEF solution soon, if there’s any interest.
3.)What happens when you have multiple projects with 1 client? This is a good point. I hadn’t considered this, but there *should be an easy workaround, which I have yet to test. It would require changing the structure a bit of things. Basically I would use a custom member profile field and use that to hold the project name they are involved in, and then furthermore use that to determine url_title of the entry. Sorry if that makes little sense, but I’m still wrapping my head around a solution, and hope to have one soon, as this is definitely a solve-able issue. If this way doesn’t work, I’ll most likely use some javascript hackery to display appropriate projects on a landing page before continuing to a detail page. I’ll update here with more code soon.
4.)Does each client get a dedicated upload location? Doing it the way I have done so far, they do not, as it is unnecessary in my own opinion. However I might look further at this in the future. An easy way to do so if you required would be to create a file upload location for each client, and then in the code make some small changes. For example, for client A we would use /image_bin_a/ extension, and for client B we’d use /image_bin_b/, and then in the ‘image’ and ‘file’ templates, make a change like:
switch ($url_path):
case "image_bin_a":
$path = 'clients/client_a/images';
break;
case "image_bin_b":
$path = 'clients/client_b/images';
break;
default:
exit();
endswitch;I’m not sure why this would be necessary, but it is certainly possible.
5.)Why use Mark’s ext. if EE2 has a file upload capability? Because I am a poor, wretched soul who has yet to upgrade to EE2. Please don’t stone me. The shame is already too much. The tears of a thousand nights fall silently on my version 1.6.9 stone prison floor…. Seriously though, I didn’t know EE2 had this capability, and if it does, this solution is even easier. I don’t have the ability to upgrade to EE2 yet. But I may do so soon. On a side note, is it pretty nice? Haven’t even played with it yet =[
6.)Why use PHP if EE has native way of getting loggin user and username? This one is quite simple. You can’t use {username} inside of the weblog entries tag for url_title. It simply doesn’t work, I believe because of a parse order problem.
Consider the following, as I believe it is what you are referring to:
{exp:weblog:entries weblog="clients" rdf="off" dynamic="off" url_title="{username}"}The template would not function with the above code.
In response to: “Nice thing about EE is that if offers many ways to get the same thing done I am just wondering why such a non-EE way of doing this…”
I would greatly prefer to do it the EE way. But unfortunately I don’t believe EE, as of version 1.6.9 (my version) has support for this functionality, especially when it can be created with some small steps. If there is a more EE integrated way of doing it I would love for people to share, because I wasn’t able to find one in my limited time of digging yesterday and today. I too love how EE gives us a multitude of ways to solve a single problem, but in this case at least, I couldn’t find an EE inclusive way of doing it. If anyone knows of one, please feel free to post and prove my ignorance 😊
As an additional note, I have done my client system using EE 1.6.9, and some of the code may be superfluous if you have an upgraded installation running 2.X
Lastly, I was wondering if there would be any interest in a downloadable package, that would simplify this process. If there’s anyone out there who thinks this is a good idea let me know. I can try my hand at creating a system to allow this functionality in a much easier to setup way. Noncommercial of course, and open source. I want to help the EE community, as it has helped me a ton.
To address the issue I previously touched on:
3.)What happens when you have multiple projects with 1 client?
The solution to this is so simple, but it took me a good half-hour to figure it out sadly. The way I have done it is by creating another custom field, text field, shortname {client_name} .
Then you just filter weblog entries by shortname {client_name} that matches the {username}. I’ll post the code in a few minutes. Thanks again for bringing this dilemma to my attention.
Ok so here is what I have, or at least a very watered down version that is easy to understand, I hope.
<head>
<title>Logiq Design || {username} Client Area</title>
<!-- rest of your xhtml head here, including css and javascript, meta, etc. -->
</head>
<body>
<!-- All of your regular code and such goes here. -->
<?php
global $SESS;
$logged_username = $SESS->userdata['username'];
?>
<ul class="project_nav">
{exp:weblog:entries weblog="clients" rdf="off" dynamic="off"}
{if "<?php echo $logged_username;?>" == "{client_name}"}
<li><a href="#{project_title}">{project_title}</a></li>
{/if}
{/exp:weblog:entries}
{exp:weblog:entries weblog="clients" rdf="off" dynamic="off"}
{if "<?php echo $logged_username;?>" == "{client_name}"}
<div class="client_project" id="{project_title}">
<h1>{project_title} Image Previews</h1>
<!-- After we add the code to the 'image' template, the following code will output the image in its place here. If a user views the source or the image location, or tries to copy and paste it into their browser, it won't function. It will only work inside this template. The user can still save the image, but they can't get the direct path to the image. !-->
{project_previews}
{path=clients/image}{file_name}/image_bin/
{/project_previews}
<h1>{project_title} Deliverables</h1>
<!-- the following code is similar to the one we used for the project image previews, it relies on the file field but this time the 'file' template. !-->
{project_deliverables}
<a href="http://{path=clients/file}{file_name}/file_bin/">{file_name}</a>
{/project_deliverables}
</div> <!-- ends the 'client_project' div !-->
{/if}
{/exp:weblog:entries}I’m still not positive this is the best solution, and am testing it now. I’ll let you know what I find security and efficiency wise.
It may end up being better to change the index template to a listing of projects available to the client, and then further direct them to a page for each project. This current setup just shows all of the posts that have the {client_name} variable matching their {username}.
If anyone else has ideas please feel free to contribute.
Sure. The image appears just as a regular image would on the page. You can optionally specify size, and link to a larger version.
Lets say for example I uploaded a file named LogiqDesignLogo.png to my clients/client_images/ directory.
The real path to the image would be “http://www.mysite.com/clients/client_images/LogiqDesignLogo.png”
Heres the key:
The code generated from the script would show the following in the page xhtml:
http://www.mysite.com/index.php/clients/image/LogiqDesignLogo.png/image_bin/Notice it isn’t the true path to the image. If the user takes that path and copies it into their browser, it will not work. The only way I know off to see the image is to be logged into the site, on the page viewing it, or have done so and saved it to their hard-disk.
The PHP creates the image or the file on the fly and relays it to the user. Because PHP bypasses .htaccess, I can password protect the http://www.mysite.com/clients/client_images/ directory in case anyone ever guessed it, and they would have to login to see anything contained within.
I’ve attached a screenshot of how it looks on my site to this post.
To see it live: I have created a demo account on my site for you to see the functionality. http://www.logiqdesign.com Username: demo password: eeforums Once logged in you will be redirected to the client area where you can view DemoProject’s associated project information.
Thanks Alexander for the detail response. I have given this thought in the past but have really done nothing about it…so hopefully you did not take my questions as interrogation but as suggestive questions 😊 4.) not really need just some of us anal about structure and separation but certainly not a must have
5.) lol there is plenty of users running 1.6.9 quite happily so don’t worry your work is not wasted.
6.) if you are using a logic of “url_title=”{username}” then I believe you are right. What I have done in past is use a few more templates starting the chain with:
<a href="http://{path=/mycontent/}{member_id}" title="My Content">My Content</a>then on ‘mycontent’ template or index of that template group..running a query based on segment_2 = member_id:
{exp:query sql="SELECT exp_comments.author_id, exp_weblog_titles.title, exp_weblog_titles.url_title, exp_comments.comment, exp_comments.comment_date, exp_weblog_titles.comment_total, exp_weblog_titles.recent_comment_date as comment_recent FROM exp_comments INNER JOIN exp_weblog_titles ON (exp_comments.entry_id = exp_weblog_titles.entry_id) WHERE exp_comments.author_id ='{segment_2}'"}
<a href="http://{path=">{title}</a>
{comment}
{comment_date format='%m/%d/%y'}
{comment_total}</ br>
{comment_recent format='%m/%d/%y'}
{/exp:query}on the ‘view’ template we can then use the same concept of query to check if author of entry = memeber_id to allow the view of the content.
Have not had a chance to test your code but do post updates, plenty of people will appreciate it 😉 All the best!
Hello Alexander,
Thank you for making this contribute.
How do customers have to download /select multiple, or all, the images which are placed on the client page? For instance we have lot’s of projects where there are at least 100 pictures which have to be downloaded to the client. It would be nice if the customer can easily select per image or choose to select all and the press a download button. What’s your idea?
@lebisol
Thanks so much for the comments. I hadn’t considered the way you showed in #6, and I think that may be a viable and useful solution as well.
As for being anal about organization, I can only say that once I added the capability in the code, I found myself creating tons of directories to separate by client, and am in love with the directory structure now hehe. So thanks for that suggestion as well.
@ddewit
From my understanding the best setup would be to show the user a ton of thumbnails, have them select the ones they want, probably with checkboxes, and then hit download.
This might take me a bit, but I’ll look at a solution for this today. Off the top of my head I think it can be done, but I’m not positive on how to achieve it in an elegant way as of yet. But again, let me take a look and see what I can come up with.
Okay, still working on this Client System.
The demo is still live:
http://www.logiqdesign.com Username: demo Password: eeforums
I have attached a zip to this post containing very watered down, basic versions of my client center templates. I’ll add further documentation in the future, but for those who need an immediate straightforward solution, this may be what you are looking for. Remember all of these templates need to go into a new template group, named ‘clients’, on your site.
To make use of all the custom fields, here are the details. {client_name} Text input Username of the client the project corresponds to.
{project_title} Text Input Name of the project.
{project_overview} Textarea, for putting in an overview of the client’s project.
{project_status} Textarea, for updating with the current status of the project.
{project_previews} File. (requires mark huot’s file extension currently for EE 1.6.9) This is for image files.
{project_downloads} File. (requires mark huot’s file extension currently for EE 1.6.9) This is for other file types, like PDF or zip or whatever.
I plan on really fleshing out the idea of this system. Updates from the original post that are in the download include:
The index is now a landing page, showing the client projects that they are associated with, with a link for each to a more detailed page.
The detail page is used to display specific projects’ information, and is restricted by client name. It allows only client_name and admin to see the weblog entry by using conditionals. For example, only admin and a user named ‘nike’ could view a project entry where the {client_name} variable was ‘nike’. Other users would see nothing.
The admin page allows a listing of all the clients and projects you have created, and lists their status with a link to the detail page for that project. The admin page should be restricted access though your templating preferences to allow NOBODY to view. Super admins will still be able to view it.
May I ask what you version of EE you are using?
My solution is currently on 1.6.9, and uses Mark Huot’s file upload extension. To my knowledge this allows for an unlimited number of uploads, all done in one weblog entry, on one page, and hitting submit once.
If you are on 2.0 I will be able to have an answer soon, as I have just purchased an EE Freelancer license and will be putting it on a test server tonight to play with. Since from what I’ve read 2.0 supports file uploads, I wouldn’t imagine having multiple uploads would be a problem, though again I can’t say for sure since I haven’t tested. I’ll have an answer on this one soon as well.
Packet Tide owns and develops ExpressionEngine. © Packet Tide, All Rights Reserved.