Skip to content
Snippets Groups Projects
Commit 045ff994 authored by Allen Gilliland's avatar Allen Gilliland
Browse files

Merge pull request #423 from metabase/orgless

Orgless Metabase
parents af199ffb e9f7a57e
No related branches found
No related tags found
No related merge requests found
Showing
with 121 additions and 269 deletions
......@@ -5,6 +5,8 @@ var DatabasesControllers = angular.module('corvusadmin.databases.controllers', [
DatabasesControllers.controller('DatabaseList', ['$scope', 'Metabase', function($scope, Metabase) {
$scope.databases = [];
$scope.delete = function(databaseId) {
if ($scope.databases) {
......@@ -20,27 +22,10 @@ DatabasesControllers.controller('DatabaseList', ['$scope', 'Metabase', function(
}
};
$scope.$watch('currentOrg', function(org) {
if (org) {
$scope.databases = [];
Metabase.db_list({
'orgId': org.id
}, function(databases) {
// if we are an org that 'inherits' lets only show our our own dbs in this view
if (org.inherits) {
var dm = _.filter(databases, function(database) {
return database.organization.id === org.id;
});
$scope.databases = dm;
} else {
$scope.databases = databases;
}
}, function(error) {
console.log('error getting database list', error);
});
}
Metabase.db_list(function(databases) {
$scope.databases = databases;
}, function(error) {
console.log('error getting database list', error);
});
}]);
......@@ -70,11 +55,10 @@ DatabasesControllers.controller('DatabaseEdit', ['$scope', '$routeParams', '$loc
// create a new Database
var create = function(database, details, redirectToDetail) {
$scope.$broadcast("form:reset");
database.org = $scope.currentOrg.id;
database.details = $scope.ENGINES[database.engine].buildDetails(details);
return Metabase.db_create(database).$promise.then(function(new_database) {
if (redirectToDetail) {
$location.path('/' + $scope.currentOrg.slug + '/admin/databases/' + new_database.id);
$location.path('/admin/databases/' + new_database.id);
}
$scope.$broadcast("form:api-success", "Successfully created!");
$scope.$emit("database:created", new_database);
......
......@@ -5,23 +5,23 @@ var AdminDatabases = angular.module('corvusadmin.databases', [
]);
AdminDatabases.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/:orgSlug/admin/databases', {
$routeProvider.when('/admin/databases', {
templateUrl: '/app/admin/databases/partials/database_list.html',
controller: 'DatabaseList'
});
$routeProvider.when('/:orgSlug/admin/databases/create', {
$routeProvider.when('/admin/databases/create', {
templateUrl: '/app/admin/databases/partials/database_edit.html',
controller: 'DatabaseEdit'
});
$routeProvider.when('/:orgSlug/admin/databases/:databaseId', {
redirectTo: '/:orgSlug/admin/databases/:databaseId/tables'
$routeProvider.when('/admin/databases/:databaseId', {
redirectTo: '/admin/databases/:databaseId/tables'
});
$routeProvider.when('/:orgSlug/admin/databases/:databaseId/:mode', {
$routeProvider.when('/admin/databases/:databaseId/:mode', {
templateUrl: '/app/admin/databases/partials/database_master_detail.html',
controller: 'DatabaseMasterDetail'
});
$routeProvider.when('/:orgSlug/admin/databases/:databaseId/:mode/:tableId', {
$routeProvider.when('/admin/databases/:databaseId/:mode/:tableId', {
templateUrl: '/app/admin/databases/partials/database_master_detail.html',
controller: 'DatabaseMasterDetail'
});
......
<div class="wrapper">
<section class="Breadcrumbs">
<a class="Breadcrumb Breadcrumb--path" cv-org-href="/admin/databases/">Databases</a>
<a class="Breadcrumb Breadcrumb--path" href="/admin/databases/">Databases</a>
<mb-icon name="chevronright" class="Breadcrumb-divider" width="12px" height="12px"></mb-icon>
<h2 class="Breadcrumb Breadcrumb--page" ng-if="!database.id">Add Database</h2>
<h2 class="Breadcrumb Breadcrumb--page" ng-if="database.id">{{database.name}}</h2>
......
<div class="wrapper">
<section class="PageHeader clearfix">
<a class="Button Button--primary float-right" cv-org-href="/admin/databases/create">Add database</a>
<a class="Button Button--primary float-right" href="/admin/databases/create">Add database</a>
<h2 class="PageTitle">Databases</h2>
</section>
......@@ -22,7 +22,7 @@
</tr>
<tr ng-repeat="database in databases">
<td>
<a class="text-bold link" cv-org-href="/admin/databases/{{database.id}}/tables">{{database.name}}</a>
<a class="text-bold link" href="/admin/databases/{{database.id}}/tables">{{database.name}}</a>
</td>
<td>
{{database.engine}}
......
......@@ -2,8 +2,8 @@
<section class="my3 clearfix">
<div class="py2 float-right">
<div class="Button-group Button-group--blue text-uppercase text-bold">
<a class="Button AdminHoverItem" ng-class="{ 'Button--active': pane == 'tables' }" cv-org-href="/admin/databases/{{database.id}}/tables/{{table.id}}">Data</a>
<a class="Button AdminHoverItem" ng-class="{ 'Button--active': pane == 'settings' }" cv-org-href="/admin/databases/{{database.id}}/settings">Connection Details</a>
<a class="Button AdminHoverItem" ng-class="{ 'Button--active': pane == 'tables' }" href="/admin/databases/{{database.id}}/tables/{{table.id}}">Data</a>
<a class="Button AdminHoverItem" ng-class="{ 'Button--active': pane == 'settings' }" href="/admin/databases/{{database.id}}/settings">Connection Details</a>
</div>
</div>
<h3 class="py2">{{database.name}}</h2>
......
......@@ -9,7 +9,7 @@
<h3>Loading ...</h3>
</li>
<li class="DatabaseListItem AdminHoverItem rounded py1 px2 text-grey-4" ng-class="{ 'DatabaseListItem--active': t.id == table.id }" ng-repeat="t in tables | filter:tableSearchText">
<a class="link link--nohover text-current" cv-org-href="/admin/databases/{{database.id}}/tables/{{t.id}}">
<a class="link link--nohover text-current" href="/admin/databases/{{database.id}}/tables/{{t.id}}">
<h4>{{t.name}}</h4>
</a>
</li>
......
"use strict";
/*global btoa*/
/*global $*/
var OrganizationAdminControllers = angular.module('corvusadmin.organization.controllers', [
'corvus.services',
'metabase.forms'
]);
OrganizationAdminControllers.controller('OrganizationSettings', ['$scope', 'Organization',
function($scope, Organization) {
$scope.save = function(organization) {
$scope.$broadcast("form:reset");
// use NULL for unset timezone
if (!organization.report_timezone) {
organization.report_timezone = null;
}
Organization.update(organization, function (org) {
$scope.currentOrg = org;
$scope.$broadcast("form:api-success", "Successfully saved!");
// we need to trigger a refresh of $scope.user so that these changes propogate the UI
$scope.refreshCurrentUser();
}, function (error) {
$scope.$broadcast("form:api-error", error);
});
};
Organization.form_input(function (result) {
$scope.form_input = result;
}, function (error) {
console.log('error getting form input', error);
});
}
]);
\ No newline at end of file
'use strict';
var OrganizationAdmin = angular.module('corvusadmin.organization', [
'corvusadmin.organization.controllers'
]);
OrganizationAdmin.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/:orgSlug/admin/org/', {
templateUrl: '/app/admin/organization/partials/org_settings.html',
controller: 'OrganizationSettings'
});
}]);
\ No newline at end of file
<div class="wrapper">
<section class="PageHeader">
<h2 class="PageTitle">Organization</h2>
</section>
<section class="Grid Grid--gutters Grid--full Grid--2of3">
<!-- form -->
<div class="Grid-cell Cell--2of3">
<form class="Form-new bordered rounded shadowed" name="form" novalidate>
<div class="Form-field" mb-form-field="slug">
<mb-form-label display-name="Slug" field-name="slug"></mb-form-label>
<input class="Form-input Form-offset full" name="slug" ng-model="currentOrg.slug" ng-disabled="true" />
</div>
<div class="Form-field" mb-form-field="name">
<mb-form-label display-name="Name" field-name="name"></mb-form-label>
<input class="Form-input Form-offset full" name="name" placeholder="What is the name of your organization?" ng-model="currentOrg.name" required autofocus/>
<span class="Form-charm"></span>
</div>
<div class="Form-field" mb-form-field="description">
<mb-form-label display-name="Description" field-name="description"></mb-form-label>
<input class="Form-input Form-offset full" name="description" placeholder="What else should people know about this org?" ng-model="currentOrg.description" />
<span class="Form-charm"></span>
</div>
<div class="Form-field" mb-form-field="logo_url">
<mb-form-label display-name="Logo url" field-name="logo_url"></mb-form-label>
<input class="Form-input Form-offset full" name="logo_url" placeholder="Link to an image of your logo" ng-model="currentOrg.logo_url" />
<span class="Form-charm"></span>
</div>
<div class="Form-field" mb-form-field="report_timezone">
<mb-form-label display-name="Reporting timezone" field-name="report_timezone"></mb-form-label>
<label class="Select Form-offset">
<select ng-model="currentOrg.report_timezone" ng-options="tz for tz in form_input['timezones']">
<option value="">--- default timezone ---</option>
</select>
</label>
</div>
<div class="Form-actions">
<button class="Button" ng-class="{'Button--primary': form.$valid}" ng-click="save(currentOrg)" ng-disabled="!form.$valid">
Save
</button>
<mb-form-message form="form"></mb-form-message>
</div>
</form>
</div>
</section>
</div>
<div class="wrapper">
<section class="PageHeader clearfix">
<a class="Button Button--primary float-right" cv-org-href="/admin/people/add">Add person</a>
<a class="Button Button--primary float-right" href="/admin/people/add">Add person</a>
<h2 class="PageTitle">People</h2>
</section>
......@@ -13,17 +13,17 @@
</tr>
</thead>
<tbody>
<tr ng-repeat="perm in people">
<tr ng-repeat="user in people">
<td>
{{perm.user.common_name}} <span ng-show="perm.admin" class="AdminBadge">Admin</span>
{{user.common_name}} <span ng-show="user.is_superuser" class="AdminBadge">Admin</span>
</td>
<td class="Table-actions">
<button class="Button" ng-click="toggle(perm.user.id)">
<span ng-show="perm.admin">Revoke</span>
<span ng-show="!perm.admin">Grant</span>
<button class="Button" ng-click="toggle(user.id)">
<span ng-show="user.is_superuser">Revoke</span>
<span ng-show="!user.is_superuser">Grant</span>
admin
</button>
<button class="Button Button--danger" ng-click="delete(perm.user.id)" delete-confirm>Remove</button>
<button class="Button Button--danger" ng-click="delete(user.id)" delete-confirm>Remove</button>
</td>
</tr>
</tbody>
......
<div class="wrapper">
<section class="Breadcrumbs">
<a class="Breadcrumb Breadcrumb--path" cv-org-href="/admin/people/">People</a>
<a class="Breadcrumb Breadcrumb--path" href="/admin/people/">People</a>
<mb-icon name="chevronright" class="Breadcrumb-divider" width="12px" height="12px"></mb-icon>
<h2 class="Breadcrumb Breadcrumb--page">Add person</h2>
</section>
......
......@@ -6,103 +6,93 @@ var PeopleControllers = angular.module('corvusadmin.people.controllers', [
'metabase.forms'
]);
PeopleControllers.controller('PeopleList', ['$scope', 'Organization',
function($scope, Organization) {
PeopleControllers.controller('PeopleList', ['$scope', 'User',
function($scope, User) {
// grant admin permission for a given user
var grant = function(userId) {
Organization.member_update({
orgId: $scope.currentOrg.id,
userId: userId,
admin: true
}, function (result) {
$scope.people.forEach(function (perm) {
if (perm.user.id === userId) {
perm.admin = true;
// grant superuser permission for a given user
var grant = function(user) {
user.is_superuser = true;
User.update(user, function (result) {
$scope.people.forEach(function (u) {
if (u.id === user.id) {
u.is_superuser = true;
}
});
}, function (error) {
console.log('error', error);
$scope.alertError('failed to grant admin to user');
$scope.alertError('failed to grant superuser to user');
});
};
// revoke admin permission for a given user
var revoke = function(userId) {
Organization.member_update({
orgId: $scope.currentOrg.id,
userId: userId,
admin: false
}, function (result) {
$scope.people.forEach(function (perm) {
if (perm.user.id === userId) {
perm.admin = false;
// revoke superuser permission for a given user
var revoke = function(user) {
user.is_superuser = false;
User.update(user, function (result) {
$scope.people.forEach(function (u) {
if (u.id === user.id) {
u.is_superuser = false;
}
});
}, function (error) {
console.log('error', error);
$scope.alertError('failed to revoke admin from user');
$scope.alertError('failed to revoke superuser from user');
});
};
// toggle admin permission for a given user
// toggle superuser permission for a given user
$scope.toggle = function(userId) {
$scope.people.forEach(function (perm) {
if (perm.user.id === userId) {
if (perm.admin) {
revoke(userId);
$scope.people.forEach(function (user) {
if (user.id === userId) {
if (user.is_superuser) {
revoke(user);
} else {
grant(userId);
grant(user);
}
}
});
};
// completely remove a given user (from the current org)
// completely remove a given user
// TODO: we need this api function now
$scope.delete = function(userId) {
Organization.member_remove({
orgId: $scope.currentOrg.id,
User.delete({
userId: userId
}, function(result) {
for (var i = 0; i < $scope.people.length; i++) {
if($scope.people[i].user.id === userId) {
if($scope.people[i].id === userId) {
$scope.people.splice(i, 1);
break;
}
}
}, function (error) {
console.log('error', error);
$scope.alertError('failed to remove user from org');
$scope.alertError('failed to remove user');
});
};
$scope.$watch('currentOrg', function (org) {
if (!org) return;
Organization.members({
'orgId': org.id
}, function (result) {
$scope.people = result;
}, function (error) {
console.log('error', error);
});
User.list(function (result) {
$scope.people = result;
}, function (error) {
console.log('error', error);
});
}
]);
PeopleControllers.controller('PeopleAdd', ['$scope', '$location', 'Organization',
function($scope, $location, Organization) {
PeopleControllers.controller('PeopleAdd', ['$scope', '$location', 'User',
function($scope, $location, User) {
$scope.save = function(newUser) {
$scope.$broadcast("form:reset");
newUser.orgId = $scope.currentOrg.id;
newUser.admin = false;
Organization.member_create(newUser, function (result) {
newUser.is_superuser = false;
// TODO: we need this function!!
User.create(newUser, function (result) {
// just go back to people listing page for now
$location.path('/'+$scope.currentOrg.slug+'/admin/people/');
$location.path('/admin/people/');
}, function (error) {
$scope.$broadcast("form:api-error", error);
});
......
'use strict';
var Organization = angular.module('corvusadmin.people', [
var AdminPeople = angular.module('corvusadmin.people', [
'corvusadmin.people.controllers'
]);
Organization.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/:orgSlug/admin/people/', {
AdminPeople.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/admin/people/', {
templateUrl: '/app/admin/people/partials/people.html',
controller: 'PeopleList'
});
$routeProvider.when('/:orgSlug/admin/people/add', {
$routeProvider.when('/admin/people/add', {
templateUrl: '/app/admin/people/partials/people_add.html',
controller: 'PeopleAdd'
});
......
'use strict';
/*global _*/
var SettingsAdminControllers = angular.module('superadmin.settings.controllers', ['superadmin.settings.services']);
var SettingsAdminControllers = angular.module('corvusadmin.settings.controllers', ['corvusadmin.settings.services']);
SettingsAdminControllers.controller('SettingsAdminController', ['$scope', 'SettingsAdminServices',
function($scope, SettingsAdminServices) {
......
'use strict';
var SettingsAdmin = angular.module('superadmin.settings', [
'superadmin.settings.controllers',
'superadmin.settings.services'
var SettingsAdmin = angular.module('corvusadmin.settings', [
'corvusadmin.settings.controllers',
'corvusadmin.settings.services'
]);
SettingsAdmin.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/superadmin/settings/', {
templateUrl: '/app/superadmin/settings/partials/settings.html',
$routeProvider.when('/admin/settings/', {
templateUrl: '/app/admin/settings/partials/settings.html',
controller: 'SettingsAdminController'
});
}]);
'use strict';
var SettingsAdminServices = angular.module('superadmin.settings.services', ['ngResource']);
var SettingsAdminServices = angular.module('corvusadmin.settings.services', ['ngResource']);
SettingsAdminServices.factory('SettingsAdminServices', ['$resource', function($resource) {
return $resource('/api/setting', {}, {
......@@ -13,12 +13,18 @@ SettingsAdminServices.factory('SettingsAdminServices', ['$resource', function($r
// POST endpoint handles create + update in this case
put: {
url: '/api/setting/:key',
method: 'PUT'
method: 'PUT',
params: {
key: '@key'
}
},
delete: {
url: '/api/setting/:key',
method: 'DELETE'
method: 'DELETE',
params: {
key: '@key'
}
}
});
}]);
......@@ -25,11 +25,9 @@ var Corvus = angular.module('corvus', [
'corvus.reserve',
'corvus.user',
'corvus.setup',
'corvusadmin.organization',
'corvusadmin.databases',
'corvusadmin.people',
'superadmin.settings',
'superadmin.organization'
'corvusadmin.settings'
]);
Corvus.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$locationProvider.html5Mode({
......@@ -58,31 +56,12 @@ Corvus.config(['$routeProvider', '$locationProvider', function($routeProvider, $
}
});
$routeProvider.when('/superadmin/', {
$routeProvider.when('/admin/', {
redirectTo: function(routeParams, path, search) {
return '/superadmin/settings/';
return '/admin/settings';
}
});
// TODO: we need actual homepages for orgs!
$routeProvider.when('/:orgSlug/', {
redirectTo: function(routeParams, path, search) {
return '/' + routeParams.orgSlug + '/dash/';
}
});
$routeProvider.when('/:orgSlug/admin/', {
redirectTo: function(routeParams, path, search) {
return '/' + routeParams.orgSlug + '/admin/org/';
}
});
// admin routes
$routeProvider.when('/:orgSlug/admin/test_login_form', {
templateUrl: '/app/admin/test_login_form.html',
controller: 'TestLoginForm'
});
// TODO: we need an appropriate homepage or something to show in this situation
$routeProvider.otherwise({
redirectTo: '/user/edit_current'
......
......@@ -31,17 +31,12 @@ CardControllers.controller('CardList', ['$scope', '$location', 'Card', function(
$scope.filter = function(filterMode) {
$scope.filterMode = filterMode;
$scope.$watch('currentOrg', function(org) {
if (!org) return;
Card.list({
'orgId': org.id,
'filterMode': filterMode
}, function(cards) {
$scope.cards = cards;
}, function(error) {
console.log('error getting cards list', error);
});
Card.list({
'filterMode': filterMode
}, function(cards) {
$scope.cards = cards;
}, function(error) {
console.log('error getting cards list', error);
});
};
......@@ -126,7 +121,7 @@ CardControllers.controller('CardDetail', [
cardJson = JSON.stringify(card);
// for new cards we redirect the user
$location.path('/' + $scope.currentOrg.slug + '/card/' + newCard.id);
$location.path('/card/' + newCard.id);
},
notifyCardUpdatedFn: function(updatedCard) {
cardJson = JSON.stringify(card);
......@@ -334,7 +329,6 @@ CardControllers.controller('CardDetail', [
}, function (result) {
if (cloning) {
result.id = undefined; // since it's a new card
result.organization = $scope.currentOrg.id;
result.carddirty = true; // so it cand be saved right away
}
......@@ -367,7 +361,6 @@ CardControllers.controller('CardDetail', [
} else {
// starting a new card
card.organization = $scope.currentOrg.id;
// this is just an easy way to ensure defaults are all setup
headerModel.setQueryModeFn("query");
......@@ -397,32 +390,21 @@ CardControllers.controller('CardDetail', [
React.unmountComponentAtNode(document.getElementById('react_qb_viz'));
});
// TODO: we should get database list first, then do rest of setup
// because without databases this UI is meaningless
$scope.$watch('currentOrg', function (org) {
// we need org always, so we just won't do anything if we don't have one
if (!org) {return;}
// TODO: while we wait for the databases list we should put something on screen
// grab our database list, then handle the rest
Metabase.db_list({
'orgId': org.id
}, function (dbs) {
databases = dbs;
if (dbs.length < 1) {
// TODO: some indication that setting up a db is required
return;
}
// TODO: while we wait for the databases list we should put something on screen
// grab our database list, then handle the rest
Metabase.db_list(function (dbs) {
databases = dbs;
// finish initializing our page and render
initAndRender();
if (dbs.length < 1) {
// TODO: some indication that setting up a db is required
return;
}
}, function (error) {
console.log('error getting database list', error);
});
// finish initializing our page and render
initAndRender();
}, function (error) {
console.log('error getting database list', error);
});
}
]);
......@@ -14,15 +14,15 @@ var Card = angular.module('corvus.card', [
]);
Card.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/:orgSlug/card/', {
$routeProvider.when('/card/', {
templateUrl: '/app/card/partials/card_list.html',
controller: 'CardList'
});
$routeProvider.when('/:orgSlug/card/create/', {
$routeProvider.when('/card/create/', {
templateUrl: '/app/card/partials/card_detail.html',
controller: 'CardDetail'
});
$routeProvider.when('/:orgSlug/card/:cardId', {
$routeProvider.when('/card/:cardId', {
templateUrl: '/app/card/partials/card_detail.html',
controller: 'CardDetail'
});
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment