Commit 6e4d5175 authored by Richard Mansfield's avatar Richard Mansfield
Browse files

Semi-public views (#1461)

parent 55db5eac
<h3>Secret URL</h3>
<p>If you don't want everyone to be able to see your View, but you
would like to show it to a few people who are not registered on this
site, then you can make your View visible to the public through a
special link which is difficult to guess. You can let your friends
know about your View by pasting the link into an email.</p>
......@@ -88,6 +88,7 @@ $string['tutors'] = 'tutors';
$string['loggedinlower'] = 'logged in users';
$string['publiclower'] = 'public';
$string['everyoneingroup'] = 'Everyone in Group';
$string['token'] = 'Secret URL';
// view user
$string['inviteusertojoingroup'] = 'Invite this user to join a group';
......
......@@ -698,6 +698,18 @@
<KEY NAME="usrfk" TYPE="foreign" FIELDS="usr" REFTABLE="usr" REFFIELDS="id" />
</KEYS>
</TABLE>
<TABLE NAME="view_access_token">
<FIELDS>
<FIELD NAME="view" TYPE="int" LENGTH="10" NOTNULL="true" />
<FIELD NAME="token" TYPE="char" LENGTH="100" NOTNULL="true" />
<FIELD NAME="startdate" TYPE="datetime" NOTNULL="false" />
<FIELD NAME="stopdate" TYPE="datetime" NOTNULL="false" />
</FIELDS>
<KEYS>
<KEY NAME="viewfk" TYPE="foreign" FIELDS="view" REFTABLE="view" REFFIELDS="id" />
<KEY NAME="primary" TYPE="primary" FIELDS="token" />
</KEYS>
</TABLE>
<!-- site content stuff -->
<!--
<TABLE NAME="site_file">
......
......@@ -1411,6 +1411,17 @@ function xmldb_core_upgrade($oldversion=0) {
}
}
if ($oldversion < 2008102200) {
$table = new XMLDBTable('view_access_token');
$table->addFieldInfo('view', XMLDB_TYPE_INTEGER, 10, false, XMLDB_NOTNULL);
$table->addFieldInfo('token', XMLDB_TYPE_CHAR, 100, XMLDB_UNSIGNED, XMLDB_NOTNULL);
$table->addFieldInfo('startdate', XMLDB_TYPE_DATETIME, null, null);
$table->addFieldInfo('stopdate', XMLDB_TYPE_DATETIME, null, null);
$table->addKeyInfo('viewfk', XMLDB_KEY_FOREIGN, array('view'), 'view', array('id'));
$table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('token'));
create_table($table);
}
return $status;
}
......
......@@ -41,7 +41,7 @@ function pieform_element_viewacl(Pieform $form, $element) {
// Look for the presets and split them into two groups
$presets = array();
if (get_config('allowpublicviews') == '1') {
$presets = array('public', 'loggedin', 'friends');
$presets = array('public', 'loggedin', 'friends', 'token');
}
else {
$presets = array('loggedin', 'friends');
......@@ -77,6 +77,8 @@ function pieform_element_viewacl(Pieform $form, $element) {
$smarty->assign('potentialpresets', json_encode($potentialpresets));
$smarty->assign('accesslist', json_encode($value));
$smarty->assign('viewid', $form->get_property('viewid'));
$smarty->assign('formname', $form->get_property('name'));
return $smarty->fetch('form/viewacl.tpl');
}
......@@ -109,7 +111,7 @@ function pieform_element_viewacl_get_value(Pieform $form, $element) {
}
if (get_config('allowpublicviews') != '1' && $values) {
foreach ($values as $key => $value) {
if ($value['type'] == 'public') {
if ($value['type'] == 'public' || $value['type'] == 'token') {
unset($values[$key]);
}
}
......
......@@ -1281,12 +1281,7 @@ function pieform_element_textarea_configure($element) {
*
* @returns boolean Wether the specified user can look at the specified view.
*/
function can_view_view($view_id, $user_id=null) {
/*
TODO PENNY PROFILEVIEW MERGE
I couldn't figure out this patch and needed to continue:
http://paste.dollyfish.net.nz/0e6800
*/
function can_view_view($view_id, $user_id=null, $token=null) {
global $USER;
$now = time();
$dbnow = db_format_timestamp($now);
......@@ -1295,9 +1290,18 @@ function can_view_view($view_id, $user_id=null) {
$user_id = $USER->get('id');
}
$publicviews = get_config('allowpublicviews');
if ($publicviews) {
if (!$token) {
$token = get_cookie('viewaccess:'.$view_id);
}
if ($token && (!$user_id || $user_id == $USER->get('id')) && $view_id == get_view_from_token($token)) {
return true;
}
}
if (!$USER->is_logged_in()) {
// check public
$publicviews = get_config('allowpublicviews');
$publicprofiles = get_config('allowpublicprofiles');
if ($publicviews || $publicprofiles) {
$public = get_record_sql("
......@@ -1380,6 +1384,22 @@ function can_view_view($view_id, $user_id=null) {
return false;
}
/* return the view associated with a given token */
function get_view_from_token($token) {
if (!$token) {
return false;
}
return get_field_sql('
SELECT view
FROM {view_access_token}
WHERE token = ?
AND (startdate IS NULL OR startdate < current_timestamp)
AND (stopdate IS NULL OR stopdate > current_timestamp)
', array($token));
}
/**
* get the views that a user can see belonging
* to the given users
......@@ -1840,4 +1860,15 @@ function recalculate_quota() {
}
}
function random_string($length=15) {
$pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$poollen = strlen($pool);
mt_srand ((double) microtime() * 1000000);
$string = '';
for ($i = 0; $i < $length; $i++) {
$string .= substr($pool, (mt_rand()%($poollen)), 1);
}
return $string;
}
?>
......@@ -27,7 +27,7 @@
defined('INTERNAL') || die();
$config = new StdClass;
$config->version = 2008101602;
$config->version = 2008102200;
$config->release = '1.1.0beta2dev';
$config->minupgradefrom = 2007080700;
$config->minupgraderelease = '0.8.0 (release tag 0.8.0_RELEASE)';
......
......@@ -282,18 +282,32 @@ class View {
public function get_access($timeformat=null) {
if (is_mysql()) {
$uid = 'usr';
$gid = '"group"';
}
else {
$uid = 'CAST (usr AS TEXT)';
$gid = 'CAST ("group" AS TEXT)';
}
$data = get_records_sql_array("
SELECT accesstype AS type, NULL AS id, NULL AS role, NULL AS grouptype, startdate, stopdate
FROM {view_access}
WHERE view = ?
UNION
SELECT 'user' AS type, usr AS id, NULL AS role, NULL AS grouptype, startdate, stopdate
SELECT 'user' AS type, $uid AS id, NULL AS role, NULL AS grouptype, startdate, stopdate
FROM {view_access_usr}
WHERE view = ?
UNION
SELECT 'group', \"group\", role, grouptype, startdate, stopdate FROM {view_access_group}
SELECT 'group', $gid, role, grouptype, startdate, stopdate FROM {view_access_group}
INNER JOIN {group} g ON (\"group\" = g.id AND g.deleted = ?)
WHERE view = ?", array($this->id, $this->id, 0, $this->id));
WHERE view = ?
UNION
SELECT 'token', token, NULL AS role, NULL AS grouptype, startdate, stopdate
FROM {view_access_token}
WHERE view = ?
", array($this->id, $this->id, 0, $this->id, $this->id));
if ($data) {
foreach ($data as &$item) {
$item = (array)$item;
......@@ -376,6 +390,7 @@ class View {
delete_records('view_access', 'view', $this->get('id'));
delete_records('view_access_usr', 'view', $this->get('id'));
delete_records('view_access_group', 'view', $this->get('id'));
delete_records('view_access_token', 'view', $this->get('id'));
$time = db_format_timestamp(time());
// View access
......@@ -403,6 +418,10 @@ class View {
}
insert_record('view_access_group', $accessrecord);
break;
case 'token':
$accessrecord->token = $item['id'];
insert_record('view_access_token', $accessrecord);
break;
}
}
}
......
......@@ -1090,9 +1090,10 @@ function get_cookie($name) {
* @param int $expires The unix timestamp of the time the cookie should expire
* @todo path/domain/secure: should be set automatically by this function if possible (?)
*/
function set_cookie($name, $value='', $expires=0, $path='', $domain='', $secure=false) {
function set_cookie($name, $value='', $expires=0) {
$name = get_config('cookieprefix') . $name;
setcookie($name, $value, $expires, $path, $domain, $secure);
$url = parse_url(get_config('wwwroot'));
setcookie($name, $value, $expires, $url['path'], $url['host'], false);
}
/**
......
......@@ -36,9 +36,20 @@ function renderPotentialPresetItem(item) {
var row = DIV(null, addButton, ' ', item.name);
item.preset = true;
connect(addButton, 'onclick', function() {
appendChildNodes('accesslist', renderAccessListItem(item));
});
if (item.type == 'token') {
connect(addButton, 'onclick', function() {
sendjsonrequest('newviewtoken.json.php', {'view': {{$viewid}}}, 'POST', function(data) {
item.id = data.data.token;
appendChildNodes('accesslist', renderAccessListItem(item));
});
});
appendChildNodes(row, contextualHelpIcon('{{$formname}}', 'secreturl', 'core', 'view', null, null));
}
else {
connect(addButton, 'onclick', function() {
appendChildNodes('accesslist', renderAccessListItem(item));
});
}
appendChildNodes('potentialpresetitems', row);
return row;
......@@ -64,6 +75,9 @@ function renderAccessListItem(item) {
cssClass += ' preset';
}
cssClass += ' ' + item.type + '-container';
if (item.type == 'token') {
item.name = config.wwwroot + 'view/view.php?t=' + item.id;
}
var name = item.name;
if (item.type == 'user') {
name = [IMG({'src': config.wwwroot + 'thumb.php?type=profileicon&id=' + item.id + '&maxwidth=20&maxheight=20'}), ' ', name];
......
......@@ -76,6 +76,7 @@ $form = array(
'renderer' => 'div',
'plugintype' => 'core',
'pluginname' => 'view',
'viewid' => $view->get('id'),
'elements' => array(
'id' => array(
'type' => 'hidden',
......
<?php
/**
* Mahara: Electronic portfolio, weblog, resume builder and social networking
* Copyright (C) 2006-2008 Catalyst IT Ltd (http://www.catalyst.net.nz)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package mahara
* @subpackage core
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
* @copyright (C) 2006-2008 Catalyst IT Ltd http://catalyst.net.nz
*
*/
define('INTERNAL', 1);
define('JSON', 1);
require(dirname(dirname(__FILE__)) . '/init.php');
$data = new StdClass;
$data->view = param_integer('view');
$data->token = random_string(20);
while (record_exists('view_access_token', 'token', $data->token)) {
$data->token = random_string(20);
}
if (!insert_record('view_access_token', $data)) {
json_reply(true, get_string('createviewtokenfailed', 'view'));
}
json_reply(false, array('message' => null, 'data' => $data));
?>
......@@ -34,13 +34,24 @@ require(dirname(dirname(__FILE__)) . '/init.php');
require(get_config('libroot') . 'view.php');
require('group.php');
$viewid = param_integer('id');
$viewtoken = get_config('allowpublicviews') ? param_alphanum('t', null) : null;
if ($viewtoken) {
if (!$viewid = get_view_from_token($viewtoken)) {
throw new AccessDeniedException();
}
if ($viewtoken != get_cookie('viewaccess:'.$viewid)) {
set_cookie('viewaccess:'.$viewid, $viewtoken);
}
}
else {
$viewid = param_integer('id');
}
$new = param_boolean('new');
$view = new View($viewid);
if (!can_view_view($viewid)) {
if (!can_view_view($viewid, null, $viewtoken)) {
throw new AccessDeniedException();
}
$view = new View($viewid);
$group = $view->get('group');
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment