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'; ...@@ -88,6 +88,7 @@ $string['tutors'] = 'tutors';
$string['loggedinlower'] = 'logged in users'; $string['loggedinlower'] = 'logged in users';
$string['publiclower'] = 'public'; $string['publiclower'] = 'public';
$string['everyoneingroup'] = 'Everyone in Group'; $string['everyoneingroup'] = 'Everyone in Group';
$string['token'] = 'Secret URL';
// view user // view user
$string['inviteusertojoingroup'] = 'Invite this user to join a group'; $string['inviteusertojoingroup'] = 'Invite this user to join a group';
......
...@@ -698,6 +698,18 @@ ...@@ -698,6 +698,18 @@
<KEY NAME="usrfk" TYPE="foreign" FIELDS="usr" REFTABLE="usr" REFFIELDS="id" /> <KEY NAME="usrfk" TYPE="foreign" FIELDS="usr" REFTABLE="usr" REFFIELDS="id" />
</KEYS> </KEYS>
</TABLE> </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 --> <!-- site content stuff -->
<!-- <!--
<TABLE NAME="site_file"> <TABLE NAME="site_file">
......
...@@ -1411,6 +1411,17 @@ function xmldb_core_upgrade($oldversion=0) { ...@@ -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; return $status;
} }
......
...@@ -41,7 +41,7 @@ function pieform_element_viewacl(Pieform $form, $element) { ...@@ -41,7 +41,7 @@ function pieform_element_viewacl(Pieform $form, $element) {
// Look for the presets and split them into two groups // Look for the presets and split them into two groups
$presets = array(); $presets = array();
if (get_config('allowpublicviews') == '1') { if (get_config('allowpublicviews') == '1') {
$presets = array('public', 'loggedin', 'friends'); $presets = array('public', 'loggedin', 'friends', 'token');
} }
else { else {
$presets = array('loggedin', 'friends'); $presets = array('loggedin', 'friends');
...@@ -77,6 +77,8 @@ function pieform_element_viewacl(Pieform $form, $element) { ...@@ -77,6 +77,8 @@ function pieform_element_viewacl(Pieform $form, $element) {
$smarty->assign('potentialpresets', json_encode($potentialpresets)); $smarty->assign('potentialpresets', json_encode($potentialpresets));
$smarty->assign('accesslist', json_encode($value)); $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'); return $smarty->fetch('form/viewacl.tpl');
} }
...@@ -109,7 +111,7 @@ function pieform_element_viewacl_get_value(Pieform $form, $element) { ...@@ -109,7 +111,7 @@ function pieform_element_viewacl_get_value(Pieform $form, $element) {
} }
if (get_config('allowpublicviews') != '1' && $values) { if (get_config('allowpublicviews') != '1' && $values) {
foreach ($values as $key => $value) { foreach ($values as $key => $value) {
if ($value['type'] == 'public') { if ($value['type'] == 'public' || $value['type'] == 'token') {
unset($values[$key]); unset($values[$key]);
} }
} }
......
...@@ -1281,12 +1281,7 @@ function pieform_element_textarea_configure($element) { ...@@ -1281,12 +1281,7 @@ function pieform_element_textarea_configure($element) {
* *
* @returns boolean Wether the specified user can look at the specified view. * @returns boolean Wether the specified user can look at the specified view.
*/ */
function can_view_view($view_id, $user_id=null) { function can_view_view($view_id, $user_id=null, $token=null) {
/*
TODO PENNY PROFILEVIEW MERGE
I couldn't figure out this patch and needed to continue:
http://paste.dollyfish.net.nz/0e6800
*/
global $USER; global $USER;
$now = time(); $now = time();
$dbnow = db_format_timestamp($now); $dbnow = db_format_timestamp($now);
...@@ -1295,9 +1290,18 @@ function can_view_view($view_id, $user_id=null) { ...@@ -1295,9 +1290,18 @@ function can_view_view($view_id, $user_id=null) {
$user_id = $USER->get('id'); $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()) { if (!$USER->is_logged_in()) {
// check public // check public
$publicviews = get_config('allowpublicviews');
$publicprofiles = get_config('allowpublicprofiles'); $publicprofiles = get_config('allowpublicprofiles');
if ($publicviews || $publicprofiles) { if ($publicviews || $publicprofiles) {
$public = get_record_sql(" $public = get_record_sql("
...@@ -1380,6 +1384,22 @@ function can_view_view($view_id, $user_id=null) { ...@@ -1380,6 +1384,22 @@ function can_view_view($view_id, $user_id=null) {
return false; 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 * get the views that a user can see belonging
* to the given users * to the given users
...@@ -1840,4 +1860,15 @@ function recalculate_quota() { ...@@ -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 @@ ...@@ -27,7 +27,7 @@
defined('INTERNAL') || die(); defined('INTERNAL') || die();
$config = new StdClass; $config = new StdClass;
$config->version = 2008101602; $config->version = 2008102200;
$config->release = '1.1.0beta2dev'; $config->release = '1.1.0beta2dev';
$config->minupgradefrom = 2007080700; $config->minupgradefrom = 2007080700;
$config->minupgraderelease = '0.8.0 (release tag 0.8.0_RELEASE)'; $config->minupgraderelease = '0.8.0 (release tag 0.8.0_RELEASE)';
......
...@@ -282,18 +282,32 @@ class View { ...@@ -282,18 +282,32 @@ class View {
public function get_access($timeformat=null) { 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(" $data = get_records_sql_array("
SELECT accesstype AS type, NULL AS id, NULL AS role, NULL AS grouptype, startdate, stopdate SELECT accesstype AS type, NULL AS id, NULL AS role, NULL AS grouptype, startdate, stopdate
FROM {view_access} FROM {view_access}
WHERE view = ? WHERE view = ?
UNION 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} FROM {view_access_usr}
WHERE view = ? WHERE view = ?
UNION 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 = ?) 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) { if ($data) {
foreach ($data as &$item) { foreach ($data as &$item) {
$item = (array)$item; $item = (array)$item;
...@@ -376,6 +390,7 @@ class View { ...@@ -376,6 +390,7 @@ class View {
delete_records('view_access', 'view', $this->get('id')); delete_records('view_access', 'view', $this->get('id'));
delete_records('view_access_usr', '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_group', 'view', $this->get('id'));
delete_records('view_access_token', 'view', $this->get('id'));
$time = db_format_timestamp(time()); $time = db_format_timestamp(time());
// View access // View access
...@@ -403,6 +418,10 @@ class View { ...@@ -403,6 +418,10 @@ class View {
} }
insert_record('view_access_group', $accessrecord); insert_record('view_access_group', $accessrecord);
break; break;
case 'token':
$accessrecord->token = $item['id'];
insert_record('view_access_token', $accessrecord);
break;
} }
} }
} }
......
...@@ -1090,9 +1090,10 @@ function get_cookie($name) { ...@@ -1090,9 +1090,10 @@ function get_cookie($name) {
* @param int $expires The unix timestamp of the time the cookie should expire * @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 (?) * @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; $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) { ...@@ -36,9 +36,20 @@ function renderPotentialPresetItem(item) {
var row = DIV(null, addButton, ' ', item.name); var row = DIV(null, addButton, ' ', item.name);
item.preset = true; item.preset = true;
connect(addButton, 'onclick', function() { if (item.type == 'token') {
appendChildNodes('accesslist', renderAccessListItem(item)); 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); appendChildNodes('potentialpresetitems', row);
return row; return row;
...@@ -64,6 +75,9 @@ function renderAccessListItem(item) { ...@@ -64,6 +75,9 @@ function renderAccessListItem(item) {
cssClass += ' preset'; cssClass += ' preset';
} }
cssClass += ' ' + item.type + '-container'; cssClass += ' ' + item.type + '-container';
if (item.type == 'token') {
item.name = config.wwwroot + 'view/view.php?t=' + item.id;
}
var name = item.name; var name = item.name;
if (item.type == 'user') { if (item.type == 'user') {
name = [IMG({'src': config.wwwroot + 'thumb.php?type=profileicon&id=' + item.id + '&maxwidth=20&maxheight=20'}), ' ', name]; name = [IMG({'src': config.wwwroot + 'thumb.php?type=profileicon&id=' + item.id + '&maxwidth=20&maxheight=20'}), ' ', name];
......
...@@ -76,6 +76,7 @@ $form = array( ...@@ -76,6 +76,7 @@ $form = array(
'renderer' => 'div', 'renderer' => 'div',
'plugintype' => 'core', 'plugintype' => 'core',
'pluginname' => 'view', 'pluginname' => 'view',
'viewid' => $view->get('id'),
'elements' => array( 'elements' => array(
'id' => array( 'id' => array(
'type' => 'hidden', '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'); ...@@ -34,13 +34,24 @@ require(dirname(dirname(__FILE__)) . '/init.php');
require(get_config('libroot') . 'view.php'); require(get_config('libroot') . 'view.php');
require('group.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'); $new = param_boolean('new');
$view = new View($viewid); if (!can_view_view($viewid, null, $viewtoken)) {
if (!can_view_view($viewid)) {
throw new AccessDeniedException(); throw new AccessDeniedException();
} }
$view = new View($viewid);
$group = $view->get('group'); $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