2013-10-17 18:46:23 +00:00
$ . fn . clipboardCopy = function ( ) {
var clip = new ZeroClipboard ( $ ( this ) , { 'moviePath' : 'static/lib/ZeroClipboard.swf' } ) ;
clip . on ( 'complete' , function ( ) {
// Resets the animation.
var elem = $ ( '#clipboardCopied' ) [ 0 ] ;
elem . style . display = 'none' ;
2013-10-22 04:40:33 +00:00
elem . classList . remove ( 'animated' ) ;
2013-10-17 18:46:23 +00:00
// Show the notification.
setTimeout ( function ( ) {
elem . style . display = 'inline-block' ;
2013-10-22 04:40:33 +00:00
elem . classList . add ( 'animated' ) ;
} , 10 ) ;
2013-10-17 18:46:23 +00:00
} ) ;
} ;
2013-09-30 23:08:24 +00:00
function getFirstTextLine ( commentString ) {
if ( ! commentString ) { return ; }
var lines = commentString . split ( '\n' ) ;
2013-10-01 23:37:33 +00:00
var MARKDOWN _CHARS = {
'#' : true ,
'-' : true ,
'>' : true ,
'`' : true
} ;
2013-09-30 23:08:24 +00:00
for ( var i = 0 ; i < lines . length ; ++ i ) {
// Skip code lines.
if ( lines [ i ] . indexOf ( ' ' ) == 0 ) {
2013-10-01 23:37:33 +00:00
continue ;
2013-09-30 23:08:24 +00:00
}
// Skip empty lines.
if ( $ . trim ( lines [ i ] ) . length == 0 ) {
continue ;
}
// Skip control lines.
if ( MARKDOWN _CHARS [ $ . trim ( lines [ i ] ) [ 0 ] ] ) {
2013-10-01 23:37:33 +00:00
continue ;
2013-09-30 23:08:24 +00:00
}
return getMarkedDown ( lines [ i ] ) ;
}
return '' ;
}
function getMarkedDown ( string ) {
return Markdown . getSanitizingConverter ( ) . makeHtml ( string || '' ) ;
}
2013-10-14 21:50:07 +00:00
function HeaderCtrl ( $scope , $location , UserService , Restangular ) {
2013-10-17 21:45:08 +00:00
$scope . $watch ( function ( ) { return UserService . currentUser ( ) ; } , function ( currentUser ) {
2013-09-26 23:59:58 +00:00
$scope . user = currentUser ;
} , true ) ;
2013-09-27 23:21:54 +00:00
2013-10-14 21:50:07 +00:00
$scope . signout = function ( ) {
var signoutPost = Restangular . one ( 'signout' ) ;
signoutPost . customPOST ( ) . then ( function ( ) {
UserService . load ( ) ;
$location . path ( '/' ) ;
} ) ;
2013-10-25 03:56:08 +00:00
} ;
$scope . appLinkTarget = function ( ) {
if ( $ ( "div[ng-view]" ) . length === 0 ) {
return "_self" ;
}
return "" ;
} ;
2013-10-14 21:50:07 +00:00
$scope . $on ( '$includeContentLoaded' , function ( ) {
// THIS IS BAD, MOVE THIS TO A DIRECTIVE
$ ( '#repoSearch' ) . typeahead ( {
name : 'repositories' ,
remote : {
url : '/api/find/repository?query=%QUERY' ,
filter : function ( data ) {
var datums = [ ] ;
for ( var i = 0 ; i < data . repositories . length ; ++ i ) {
var repo = data . repositories [ i ] ;
datums . push ( {
'value' : repo . name ,
'tokens' : [ repo . name , repo . namespace ] ,
'repo' : repo
} ) ;
}
return datums ;
}
} ,
template : function ( datum ) {
template = '<div class="repo-mini-listing">' ;
2013-10-24 21:41:37 +00:00
template += '<i class="fa fa-hdd fa-lg"></i>'
2013-10-14 21:50:07 +00:00
template += '<span class="name">' + datum . repo . namespace + '/' + datum . repo . name + '</span>'
if ( datum . repo . description ) {
template += '<span class="description">' + getFirstTextLine ( datum . repo . description ) + '</span>'
2013-10-01 23:37:33 +00:00
}
2013-10-14 21:50:07 +00:00
template += '</div>'
return template ;
} ,
2013-09-27 23:21:54 +00:00
2013-10-14 21:50:07 +00:00
} ) ;
2013-09-27 23:21:54 +00:00
2013-10-14 21:50:07 +00:00
$ ( '#repoSearch' ) . on ( 'typeahead:selected' , function ( e , datum ) {
$ ( '#repoSearch' ) . typeahead ( 'setQuery' , '' ) ;
document . location = '/repository/' + datum . repo . namespace + '/' + datum . repo . name
} ) ;
2013-09-27 23:21:54 +00:00
} ) ;
2013-09-26 23:59:58 +00:00
}
2013-10-14 21:50:07 +00:00
function SigninCtrl ( $scope , $location , $timeout , Restangular , KeyService , UserService ) {
$scope . githubClientId = KeyService . githubClientId ;
var appendMixpanelId = function ( ) {
if ( mixpanel . get _distinct _id !== undefined ) {
$scope . mixpanelDistinctIdClause = "&state=" + mixpanel . get _distinct _id ( ) ;
} else {
// Mixpanel not yet loaded, try again later
$timeout ( appendMixpanelId , 200 ) ;
}
} ;
appendMixpanelId ( ) ;
$scope . signin = function ( ) {
var signinPost = Restangular . one ( 'signin' ) ;
signinPost . customPOST ( $scope . user ) . then ( function ( ) {
$scope . needsEmailVerification = false ;
$scope . invalidCredentials = false ;
// Redirect to the landing page
UserService . load ( ) ;
$location . path ( '/' ) ;
} , function ( result ) {
$scope . needsEmailVerification = result . data . needsEmailVerification ;
$scope . invalidCredentials = result . data . invalidCredentials ;
} ) ;
} ;
$scope . sendRecovery = function ( ) {
var signinPost = Restangular . one ( 'recovery' ) ;
signinPost . customPOST ( $scope . recovery ) . then ( function ( ) {
$scope . invalidEmail = false ;
$scope . sent = true ;
} , function ( result ) {
$scope . invalidEmail = true ;
$scope . sent = false ;
} ) ;
} ;
2013-10-20 17:54:00 +00:00
$scope . status = 'ready' ;
2013-10-14 21:50:07 +00:00
} ;
2013-10-04 18:35:51 +00:00
function PlansCtrl ( $scope , UserService , PlanService ) {
$scope . plans = PlanService . planList ( ) ;
2013-10-02 22:14:51 +00:00
$scope . $watch ( function ( ) { return UserService . currentUser ( ) ; } , function ( currentUser ) {
$scope . user = currentUser ;
} , true ) ;
$scope . buyNow = function ( plan ) {
if ( $scope . user && ! $scope . user . anonymous ) {
2013-10-12 17:24:55 +00:00
document . location = '/user?plan=' + plan ;
2013-10-02 22:14:51 +00:00
} else {
$ ( '#signinModal' ) . modal ( { } ) ;
}
} ;
2013-10-11 03:42:03 +00:00
$scope . status = 'ready' ;
2013-10-02 22:14:51 +00:00
}
2013-10-11 00:53:14 +00:00
function GuideCtrl ( $scope ) {
$scope . status = 'ready' ;
2013-10-02 04:28:24 +00:00
}
2013-10-02 17:29:18 +00:00
function RepoListCtrl ( $scope , Restangular , UserService ) {
$scope . $watch ( function ( ) { return UserService . currentUser ( ) ; } , function ( currentUser ) {
$scope . user = currentUser ;
} , true ) ;
2013-09-30 23:08:24 +00:00
$scope . getCommentFirstLine = function ( commentString ) {
return getMarkedDown ( getFirstTextLine ( commentString ) ) ;
} ;
$scope . getMarkedDown = function ( string ) {
if ( ! string ) { return '' ; }
return getMarkedDown ( string ) ;
} ;
2013-10-01 20:42:20 +00:00
$scope . loading = true ;
2013-10-02 04:28:24 +00:00
$scope . public _repositories = null ;
$scope . private _repositories = null ;
// Load the list of personal repositories.
var repositoryPrivateFetch = Restangular . all ( 'repository/' ) ;
repositoryPrivateFetch . getList ( { 'public' : false , 'sort' : true } ) . then ( function ( resp ) {
$scope . private _repositories = resp . repositories ;
$scope . loading = ! ( $scope . public _repositories && $scope . private _repositories ) ;
} ) ;
2013-10-01 20:42:20 +00:00
2013-10-02 04:28:24 +00:00
// Load the list of public repositories.
var options = { 'public' : true , 'private' : false , 'sort' : true , 'limit' : 10 } ;
var repositoryPublicFetch = Restangular . all ( 'repository/' ) ;
repositoryPublicFetch . getList ( options ) . then ( function ( resp ) {
$scope . public _repositories = resp . repositories ;
$scope . loading = ! ( $scope . public _repositories && $scope . private _repositories ) ;
2013-10-01 20:42:20 +00:00
} ) ;
2013-09-24 22:21:14 +00:00
}
2013-10-28 17:22:18 +00:00
function LandingCtrl ( $scope , $timeout , $location , Restangular , UserService , KeyService ) {
2013-10-01 23:37:33 +00:00
$ ( '.form-signup' ) . popover ( ) ;
2013-09-24 22:21:14 +00:00
2013-10-01 23:37:33 +00:00
$scope . $watch ( function ( ) { return UserService . currentUser ( ) ; } , function ( currentUser ) {
2013-10-02 04:28:24 +00:00
if ( ! currentUser . anonymous ) {
2013-10-08 15:36:45 +00:00
$scope . loadMyRepos ( ) ;
2013-10-02 04:28:24 +00:00
}
2013-10-01 23:37:33 +00:00
$scope . user = currentUser ;
} , true ) ;
2013-10-10 21:32:32 +00:00
angulartics . waitForVendorApi ( mixpanel , 500 , function ( loadedMixpanel ) {
var mixpanelId = loadedMixpanel . get _distinct _id ( ) ;
$scope . github _state _clause = '&state=' + mixpanelId ;
} ) ;
2013-10-10 18:42:14 +00:00
$scope . githubClientId = KeyService . githubClientId ;
2013-10-01 23:37:33 +00:00
$scope . awaitingConfirmation = false ;
2013-10-02 02:13:43 +00:00
$scope . registering = false ;
2013-10-02 04:28:24 +00:00
$scope . getCommentFirstLine = function ( commentString ) {
return getMarkedDown ( getFirstTextLine ( commentString ) ) ;
} ;
2013-10-01 23:37:33 +00:00
$scope . register = function ( ) {
2013-10-02 02:13:43 +00:00
$ ( '.form-signup' ) . popover ( 'hide' ) ;
$scope . registering = true ;
2013-10-01 23:37:33 +00:00
var newUserPost = Restangular . one ( 'user/' ) ;
newUserPost . customPOST ( $scope . newUser ) . then ( function ( ) {
$scope . awaitingConfirmation = true ;
2013-10-02 02:13:43 +00:00
$scope . registering = false ;
2013-10-08 15:50:34 +00:00
mixpanel . alias ( $scope . newUser . username ) ;
2013-10-01 23:37:33 +00:00
} , function ( result ) {
2013-10-02 02:13:43 +00:00
$scope . registering = false ;
2013-10-01 23:37:33 +00:00
$scope . registerError = result . data . message ;
$timeout ( function ( ) {
$ ( '.form-signup' ) . popover ( 'show' ) ;
} ) ;
} ) ;
} ;
2013-10-02 04:28:24 +00:00
$scope . loadMyRepos = function ( ) {
$scope . loadingmyrepos = true ;
// Load the list of repositories.
var params = {
'limit' : 5 ,
'public' : false ,
'sort' : true
} ;
var repositoryFetch = Restangular . all ( 'repository/' ) ;
repositoryFetch . getList ( params ) . then ( function ( resp ) {
$scope . myrepos = resp . repositories ;
$scope . loadingmyrepos = false ;
} ) ;
} ;
2013-10-11 00:53:14 +00:00
$scope . status = 'ready' ;
2013-10-12 01:28:02 +00:00
browserchrome . update ( ) ;
2013-09-24 22:21:14 +00:00
}
2013-09-26 21:59:20 +00:00
2013-10-17 21:59:34 +00:00
function RepoCtrl ( $scope , Restangular , $routeParams , $rootScope , $location , $timeout ) {
2013-09-26 21:59:20 +00:00
$rootScope . title = 'Loading...' ;
2013-09-27 21:01:45 +00:00
2013-10-17 03:09:43 +00:00
// Watch for changes to the tag parameter.
$scope . $on ( '$routeUpdate' , function ( ) {
$scope . setTag ( $location . search ( ) . tag , false ) ;
} ) ;
2013-09-26 21:59:20 +00:00
$scope . editDescription = function ( ) {
2013-09-26 23:07:25 +00:00
if ( ! $scope . repo . can _write ) { return ; }
2013-09-30 23:08:24 +00:00
if ( ! $scope . markdownDescriptionEditor ) {
2013-10-01 23:37:33 +00:00
var converter = Markdown . getSanitizingConverter ( ) ;
var editor = new Markdown . Editor ( converter , '-description' ) ;
editor . run ( ) ;
$scope . markdownDescriptionEditor = editor ;
2013-09-30 23:08:24 +00:00
}
$ ( '#wmd-input-description' ) [ 0 ] . value = $scope . repo . description ;
2013-09-26 23:07:25 +00:00
$ ( '#editModal' ) . modal ( { } ) ;
2013-09-26 21:59:20 +00:00
} ;
$scope . saveDescription = function ( ) {
2013-09-26 23:07:25 +00:00
$ ( '#editModal' ) . modal ( 'hide' ) ;
2013-09-30 23:08:24 +00:00
$scope . repo . description = $ ( '#wmd-input-description' ) [ 0 ] . value ;
2013-09-26 23:07:25 +00:00
$scope . repo . put ( ) ;
2013-09-27 19:26:30 +00:00
} ;
$scope . parseDate = function ( dateString ) {
return Date . parse ( dateString ) ;
} ;
2013-09-30 23:08:24 +00:00
$scope . getCommentFirstLine = function ( commentString ) {
return getMarkedDown ( getFirstTextLine ( commentString ) ) ;
} ;
2013-10-11 00:43:37 +00:00
$scope . getTimeSince = function ( createdTime ) {
2013-10-10 04:40:18 +00:00
return moment ( $scope . parseDate ( createdTime ) ) . fromNow ( ) ;
} ;
2013-09-30 23:08:24 +00:00
$scope . getMarkedDown = function ( string ) {
if ( ! string ) { return '' ; }
return getMarkedDown ( string ) ;
} ;
2013-10-17 02:37:29 +00:00
var getDefaultTag = function ( ) {
if ( $scope . repo === undefined ) {
return undefined ;
} else if ( $scope . repo . tags . hasOwnProperty ( 'latest' ) ) {
return $scope . repo . tags [ 'latest' ] ;
} else {
for ( key in $scope . repo . tags ) {
return $scope . repo . tags [ key ] ;
}
}
} ;
2013-10-17 18:29:47 +00:00
$scope . $watch ( 'repo' , function ( ) {
if ( $scope . tree ) {
2013-10-17 21:59:34 +00:00
$timeout ( function ( ) {
$scope . tree . notifyResized ( ) ;
} ) ;
2013-10-17 18:29:47 +00:00
}
} ) ;
2013-10-30 00:54:36 +00:00
var fetchRepository = function ( ) {
var repositoryFetch = Restangular . one ( 'repository/' + namespace + '/' + name ) ;
repositoryFetch . get ( ) . then ( function ( repo ) {
$rootScope . title = namespace + '/' + name ;
$scope . repo = repo ;
$scope . setTag ( $routeParams . tag ) ;
$ ( '#copyClipboard' ) . clipboardCopy ( ) ;
$scope . loading = false ;
if ( repo . is _building ) {
startBuildInfoTimer ( repo ) ;
}
} , function ( ) {
$scope . repo = null ;
$scope . loading = false ;
$rootScope . title = 'Unknown Repository' ;
} ) ;
} ;
2013-10-26 20:03:11 +00:00
var startBuildInfoTimer = function ( repo ) {
2013-10-29 22:56:56 +00:00
if ( $scope . interval ) { return ; }
2013-10-28 17:09:22 +00:00
getBuildInfo ( repo ) ;
2013-10-29 22:56:56 +00:00
$scope . interval = setInterval ( function ( ) {
2013-10-30 00:54:36 +00:00
$scope . $apply ( function ( ) { getBuildInfo ( repo ) ; } ) ;
} , 5000 ) ;
2013-10-29 22:56:56 +00:00
$scope . $on ( "$destroy" , function ( ) {
2013-10-30 00:54:36 +00:00
cancelBuildInfoTimer ( ) ;
2013-10-29 22:56:56 +00:00
} ) ;
2013-10-26 20:03:11 +00:00
} ;
2013-10-30 00:54:36 +00:00
var cancelBuildInfoTimer = function ( ) {
if ( $scope . interval ) {
clearInterval ( $scope . interval ) ;
}
} ;
2013-09-27 21:01:45 +00:00
2013-10-26 20:03:11 +00:00
var getBuildInfo = function ( repo ) {
var buildInfo = Restangular . one ( 'repository/' + repo . namespace + '/' + repo . name + '/build/' ) ;
buildInfo . get ( ) . then ( function ( resp ) {
2013-10-30 00:54:36 +00:00
var runningBuilds = [ ] ;
for ( var i = 0 ; i < resp . builds . length ; ++ i ) {
var build = resp . builds [ i ] ;
if ( build . status != 'complete' ) {
runningBuilds . push ( build ) ;
}
}
$scope . buildsInfo = runningBuilds ;
if ( ! runningBuilds . length ) {
// Cancel the build timer.
cancelBuildInfoTimer ( ) ;
// Mark the repo as no longer building.
$scope . repo . is _building = false ;
// Reload the repo information.
fetchRepository ( ) ;
listImages ( ) ;
}
2013-10-26 20:03:11 +00:00
} ) ;
} ;
2013-10-17 02:37:29 +00:00
var listImages = function ( ) {
var imageFetch = Restangular . one ( 'repository/' + namespace + '/' + name + '/image/' ) ;
2013-10-01 23:37:33 +00:00
imageFetch . get ( ) . then ( function ( resp ) {
$scope . imageHistory = resp . images ;
2013-10-30 17:55:44 +00:00
// Dispose of any existing tree.
if ( $scope . tree ) {
$scope . tree . dispose ( ) ;
}
// Create the new tree.
2013-10-17 02:33:35 +00:00
$scope . tree = new ImageHistoryTree ( namespace , name , resp . images ,
2013-10-11 00:43:37 +00:00
$scope . getCommentFirstLine , $scope . getTimeSince ) ;
$scope . tree . draw ( 'image-history-container' ) ;
2013-10-17 02:49:37 +00:00
2013-10-17 02:44:44 +00:00
// If we already have a tag, use it
if ( $scope . currentTag ) {
$scope . tree . setTag ( $scope . currentTag . name ) ;
}
2013-10-11 00:43:37 +00:00
$ ( $scope . tree ) . bind ( 'tagChanged' , function ( e ) {
2013-10-30 17:55:44 +00:00
$scope . $apply ( function ( ) { $scope . setTag ( e . tag , true ) ; } ) ;
2013-10-11 00:43:37 +00:00
} ) ;
$ ( $scope . tree ) . bind ( 'imageChanged' , function ( e ) {
$scope . $apply ( function ( ) { $scope . setImage ( e . image ) ; } ) ;
} ) ;
2013-10-01 23:37:33 +00:00
} ) ;
2013-09-27 21:01:45 +00:00
} ;
2013-09-26 21:59:20 +00:00
2013-10-18 21:59:26 +00:00
$scope . loadImageChanges = function ( image ) {
$scope . currentImageChanges = null ;
var changesFetch = Restangular . one ( 'repository/' + namespace + '/' + name + '/image/' + image . id + '/changes' ) ;
changesFetch . get ( ) . then ( function ( changeInfo ) {
$scope . currentImageChanges = changeInfo ;
} , function ( ) {
2013-10-30 17:03:44 +00:00
$scope . currentImageChanges = { 'added' : [ ] , 'removed' : [ ] , 'changed' : [ ] } ;
2013-10-18 21:59:26 +00:00
} ) ;
} ;
$scope . getMoreCount = function ( changes ) {
if ( ! changes ) { return 0 ; }
2013-10-19 00:23:41 +00:00
var addedDisplayed = Math . min ( 5 , changes . added . length ) ;
var removedDisplayed = Math . min ( 5 , changes . removed . length ) ;
var changedDisplayed = Math . min ( 5 , changes . changed . length ) ;
2013-10-18 21:59:26 +00:00
return ( changes . added . length + changes . removed . length + changes . changed . length ) -
addedDisplayed - removedDisplayed - changedDisplayed ;
} ;
2013-10-11 00:43:37 +00:00
$scope . setImage = function ( image ) {
$scope . currentImage = image ;
2013-10-18 21:59:26 +00:00
$scope . loadImageChanges ( image ) ;
2013-10-11 00:43:37 +00:00
if ( $scope . tree ) {
$scope . tree . setImage ( $scope . currentImage . id ) ;
}
} ;
2013-10-17 03:09:43 +00:00
$scope . setTag = function ( tagName , opt _updateURL ) {
2013-10-11 00:43:37 +00:00
var repo = $scope . repo ;
2013-10-17 02:37:29 +00:00
var proposedTag = repo . tags [ tagName ] ;
if ( ! proposedTag ) {
// We must find a good default
for ( tagName in repo . tags ) {
if ( ! proposedTag || tagName == 'latest' ) {
proposedTag = repo . tags [ tagName ] ;
}
}
2013-10-17 03:09:43 +00:00
}
2013-10-17 02:37:29 +00:00
if ( proposedTag ) {
2013-10-17 02:44:44 +00:00
$scope . currentTag = proposedTag ;
2013-10-17 02:37:29 +00:00
$scope . currentImage = $scope . currentTag . image ;
2013-10-18 21:59:26 +00:00
$scope . loadImageChanges ( $scope . currentImage ) ;
2013-10-17 02:37:29 +00:00
if ( $scope . tree ) {
$scope . tree . setTag ( $scope . currentTag . name ) ;
2013-10-17 03:12:00 +00:00
}
if ( opt _updateURL ) {
$location . search ( 'tag' , $scope . currentTag . name ) ;
}
2013-10-11 00:43:37 +00:00
}
} ;
2013-10-02 05:05:36 +00:00
$scope . getTagCount = function ( repo ) {
if ( ! repo ) { return 0 ; }
var count = 0 ;
for ( var tag in repo . tags ) {
++ count ;
}
return count ;
} ;
2013-09-26 21:59:20 +00:00
var namespace = $routeParams . namespace ;
var name = $routeParams . name ;
2013-10-01 20:42:20 +00:00
$scope . loading = true ;
2013-10-11 00:43:37 +00:00
// Fetch the repo.
2013-10-30 00:54:36 +00:00
fetchRepository ( ) ;
2013-10-11 00:43:37 +00:00
// Fetch the image history.
2013-10-17 02:37:29 +00:00
listImages ( ) ;
2013-09-26 23:59:58 +00:00
}
2013-09-27 00:34:58 +00:00
2013-09-27 19:26:16 +00:00
function RepoAdminCtrl ( $scope , Restangular , $routeParams , $rootScope ) {
2013-10-17 18:46:23 +00:00
$ ( '.info-icon' ) . popover ( {
'trigger' : 'hover' ,
'html' : true
} ) ;
$ ( '#copyClipboard' ) . clipboardCopy ( ) ;
2013-09-27 19:26:16 +00:00
var namespace = $routeParams . namespace ;
var name = $routeParams . name ;
2013-10-14 21:50:07 +00:00
$scope . $on ( '$viewContentLoaded' , function ( ) {
// THIS IS BAD, MOVE THIS TO A DIRECTIVE
$ ( '#userSearch' ) . typeahead ( {
name : 'users' ,
remote : {
url : '/api/users/%QUERY' ,
filter : function ( data ) {
var datums = [ ] ;
for ( var i = 0 ; i < data . users . length ; ++ i ) {
var user = data . users [ i ] ;
datums . push ( {
'value' : user ,
'tokens' : [ user ] ,
'username' : user
} ) ;
}
return datums ;
2013-10-01 23:37:33 +00:00
}
2013-10-14 21:50:07 +00:00
} ,
template : function ( datum ) {
template = '<div class="user-mini-listing">' ;
2013-10-24 21:41:37 +00:00
template += '<i class="fa fa-user fa-lg"></i>'
2013-10-14 21:50:07 +00:00
template += '<span class="name">' + datum . username + '</span>'
template += '</div>'
return template ;
} ,
} ) ;
2013-09-28 05:23:00 +00:00
2013-10-14 21:50:07 +00:00
$ ( '#userSearch' ) . on ( 'typeahead:selected' , function ( e , datum ) {
$ ( '#userSearch' ) . typeahead ( 'setQuery' , '' ) ;
$scope . addNewPermission ( datum . username ) ;
} ) ;
2013-09-28 05:23:00 +00:00
} ) ;
2013-10-23 02:39:36 +00:00
$scope . isDownloadSupported = function ( ) {
try { return ! ! new Blob ( ) ; } catch ( e ) { }
return false ;
} ;
$scope . downloadCfg = function ( token ) {
var auth = $ . base64 . encode ( "$token:" + token . code ) ;
config = {
"https://quay.io/v1/" : {
"auth" : auth ,
"email" : ""
}
} ;
var file = JSON . stringify ( config , null , ' ' ) ;
var blob = new Blob ( [ file ] ) ;
2013-10-23 02:46:43 +00:00
saveAs ( blob , '.dockercfg' ) ;
2013-10-23 02:39:36 +00:00
} ;
2013-09-28 05:23:00 +00:00
$scope . addNewPermission = function ( username ) {
// Don't allow duplicates.
if ( $scope . permissions [ username ] ) { return ; }
// Need the $scope.apply for both the permission stuff to change and for
// the XHR call to be made.
$scope . $apply ( function ( ) {
2013-10-01 23:37:33 +00:00
$scope . addRole ( username , 'read' )
2013-09-28 05:23:00 +00:00
} ) ;
} ;
$scope . deleteRole = function ( username ) {
var permissionDelete = Restangular . one ( 'repository/' + namespace + '/' + name + '/permissions/' + username ) ;
permissionDelete . customDELETE ( ) . then ( function ( ) {
2013-10-01 23:37:33 +00:00
delete $scope . permissions [ username ] ;
2013-09-28 05:23:00 +00:00
} , function ( result ) {
2013-10-01 23:37:33 +00:00
if ( result . status == 409 ) {
$ ( '#onlyadminModal' ) . modal ( { } ) ;
} else {
$ ( '#cannotchangeModal' ) . modal ( { } ) ;
}
2013-09-28 05:23:00 +00:00
} ) ;
} ;
$scope . addRole = function ( username , role ) {
var permission = {
2013-10-01 23:37:33 +00:00
'role' : role
2013-09-28 05:23:00 +00:00
} ;
var permissionPost = Restangular . one ( 'repository/' + namespace + '/' + name + '/permissions/' + username ) ;
permissionPost . customPOST ( permission ) . then ( function ( ) {
2013-10-01 23:37:33 +00:00
$scope . permissions [ username ] = permission ;
$scope . permissions = $scope . permissions ;
2013-09-28 05:23:00 +00:00
} , function ( result ) {
2013-10-01 23:37:33 +00:00
$ ( '#cannotchangeModal' ) . modal ( { } ) ;
2013-09-28 05:23:00 +00:00
} ) ;
} ;
2013-09-27 19:26:16 +00:00
$scope . setRole = function ( username , role ) {
var permission = $scope . permissions [ username ] ;
2013-09-28 05:23:00 +00:00
var currentRole = permission . role ;
2013-09-27 19:26:16 +00:00
permission . role = role ;
2013-09-28 05:23:00 +00:00
2013-09-27 19:26:16 +00:00
var permissionPut = Restangular . one ( 'repository/' + namespace + '/' + name + '/permissions/' + username ) ;
2013-09-27 19:48:54 +00:00
permissionPut . customPUT ( permission ) . then ( function ( ) { } , function ( result ) {
2013-10-01 23:37:33 +00:00
if ( result . status == 409 ) {
permission . role = currentRole ;
$ ( '#onlyadminModal' ) . modal ( { } ) ;
} else {
$ ( '#cannotchangeModal' ) . modal ( { } ) ;
}
2013-09-27 19:48:54 +00:00
} ) ;
2013-09-27 19:26:16 +00:00
} ;
2013-10-16 18:24:10 +00:00
$scope . createToken = function ( ) {
var friendlyName = {
'friendlyName' : $scope . newToken . friendlyName
} ;
var permissionPost = Restangular . one ( 'repository/' + namespace + '/' + name + '/tokens/' ) ;
permissionPost . customPOST ( friendlyName ) . then ( function ( newToken ) {
2013-10-17 20:50:58 +00:00
$scope . newToken . friendlyName = '' ;
$scope . createTokenForm . $setPristine ( ) ;
2013-10-16 18:24:10 +00:00
$scope . tokens [ newToken . code ] = newToken ;
} ) ;
} ;
$scope . deleteToken = function ( tokenCode ) {
var deleteAction = Restangular . one ( 'repository/' + namespace + '/' + name + '/tokens/' + tokenCode ) ;
deleteAction . customDELETE ( ) . then ( function ( ) {
delete $scope . tokens [ tokenCode ] ;
} ) ;
} ;
$scope . changeTokenAccess = function ( tokenCode , newAccess ) {
var role = {
'role' : newAccess
} ;
var deleteAction = Restangular . one ( 'repository/' + namespace + '/' + name + '/tokens/' + tokenCode ) ;
deleteAction . customPUT ( role ) . then ( function ( updated ) {
$scope . tokens [ updated . code ] = updated ;
} ) ;
} ;
$scope . showToken = function ( tokenCode ) {
$scope . shownToken = $scope . tokens [ tokenCode ] ;
$ ( '#tokenmodal' ) . modal ( { } ) ;
} ;
2013-09-28 21:11:10 +00:00
$scope . askChangeAccess = function ( newAccess ) {
2013-10-01 23:37:33 +00:00
$ ( '#make' + newAccess + 'Modal' ) . modal ( { } ) ;
2013-09-28 21:11:10 +00:00
} ;
$scope . changeAccess = function ( newAccess ) {
2013-10-01 23:37:33 +00:00
$ ( '#make' + newAccess + 'Modal' ) . modal ( 'hide' ) ;
var visibility = {
'visibility' : newAccess
} ;
var visibilityPost = Restangular . one ( 'repository/' + namespace + '/' + name + '/changevisibility' ) ;
visibilityPost . customPOST ( visibility ) . then ( function ( ) {
$scope . repo . is _public = newAccess == 'public' ;
} , function ( ) {
$ ( '#cannotchangeModal' ) . modal ( { } ) ;
} ) ;
2013-09-28 21:11:10 +00:00
} ;
2013-10-01 18:14:30 +00:00
$scope . askDelete = function ( ) {
2013-10-01 23:37:33 +00:00
$ ( '#confirmdeleteModal' ) . modal ( { } ) ;
2013-10-01 18:14:30 +00:00
} ;
$scope . deleteRepo = function ( ) {
2013-10-01 23:37:33 +00:00
$ ( '#confirmdeleteModal' ) . modal ( 'hide' ) ;
var deleteAction = Restangular . one ( 'repository/' + namespace + '/' + name ) ;
deleteAction . customDELETE ( ) . then ( function ( ) {
$scope . repo = null ;
setTimeout ( function ( ) {
2013-10-12 17:24:55 +00:00
document . location = '/repository/' ;
2013-10-01 23:37:33 +00:00
} , 1000 ) ;
} , function ( ) {
$ ( '#cannotchangeModal' ) . modal ( { } ) ;
} ) ;
2013-10-01 18:14:30 +00:00
} ;
2013-10-02 05:05:36 +00:00
2013-10-01 20:42:20 +00:00
$scope . loading = true ;
2013-10-01 18:14:30 +00:00
2013-09-28 21:11:10 +00:00
// Fetch the repository information.
var repositoryFetch = Restangular . one ( 'repository/' + namespace + '/' + name ) ;
repositoryFetch . get ( ) . then ( function ( repo ) {
$scope . repo = repo ;
2013-10-16 18:24:10 +00:00
$scope . loading = ! ( $scope . permissions && $scope . repo && $scope . tokens ) ;
2013-10-01 20:42:20 +00:00
} , function ( ) {
$scope . permissions = null ;
$rootScope . title = 'Unknown Repository' ;
$scope . loading = false ;
2013-09-28 21:11:10 +00:00
} ) ;
// Fetch the permissions.
2013-09-27 19:26:16 +00:00
var permissionsFetch = Restangular . one ( 'repository/' + namespace + '/' + name + '/permissions' ) ;
permissionsFetch . get ( ) . then ( function ( resp ) {
2013-09-28 05:23:00 +00:00
$rootScope . title = 'Settings - ' + namespace + '/' + name ;
2013-09-27 19:26:16 +00:00
$scope . permissions = resp . permissions ;
2013-10-16 18:24:10 +00:00
$scope . loading = ! ( $scope . permissions && $scope . repo && $scope . tokens ) ;
2013-09-27 19:26:16 +00:00
} , function ( ) {
$scope . permissions = null ;
$rootScope . title = 'Unknown Repository' ;
2013-10-01 20:42:20 +00:00
$scope . loading = false ;
2013-09-27 19:26:16 +00:00
} ) ;
2013-10-16 18:24:10 +00:00
// Fetch the tokens.
var tokensFetch = Restangular . one ( 'repository/' + namespace + '/' + name + '/tokens/' ) ;
tokensFetch . get ( ) . then ( function ( resp ) {
$scope . tokens = resp . tokens ;
$scope . loading = ! ( $scope . permissions && $scope . repo && $scope . tokens ) ;
} , function ( ) {
$scope . tokens = null ;
$scope . loading = false ;
} ) ;
2013-10-02 04:48:03 +00:00
}
2013-10-10 17:44:34 +00:00
function UserAdminCtrl ( $scope , $timeout , Restangular , PlanService , UserService , KeyService , $routeParams ) {
2013-10-04 18:35:51 +00:00
$scope . plans = PlanService . planList ( ) ;
2013-10-02 04:48:03 +00:00
2013-10-10 17:44:34 +00:00
$scope . $watch ( function ( ) { return UserService . currentUser ( ) ; } , function ( currentUser ) {
$scope . askForPassword = currentUser . askForPassword ;
} , true ) ;
2013-10-02 05:40:11 +00:00
var subscribedToPlan = function ( sub ) {
2013-10-02 04:48:03 +00:00
$scope . subscription = sub ;
2013-10-04 18:35:51 +00:00
$scope . subscribedPlan = PlanService . getPlan ( sub . plan ) ;
2013-10-02 05:40:11 +00:00
$scope . planUsagePercent = sub . usedPrivateRepos * 100 / $scope . subscribedPlan . privateRepos ;
2013-10-04 18:35:51 +00:00
if ( sub . usedPrivateRepos > $scope . subscribedPlan . privateRepos ) {
$scope . errorMessage = 'You are using more private repositories than your plan allows, please upgrate your subscription to avoid disruptions in your service.' ;
2013-10-28 21:08:26 +00:00
} else {
$scope . errorMessage = null ;
2013-10-04 18:35:51 +00:00
}
2013-10-02 05:40:11 +00:00
$scope . planLoading = false ;
2013-10-02 06:05:53 +00:00
$scope . planChanging = false ;
2013-10-08 17:57:48 +00:00
mixpanel . people . set ( {
'plan' : sub . plan
} ) ;
2013-10-28 21:08:26 +00:00
} ;
2013-10-02 05:40:11 +00:00
$scope . planLoading = true ;
var getSubscription = Restangular . one ( 'user/plan' ) ;
getSubscription . get ( ) . then ( subscribedToPlan , function ( ) {
// User has no subscription
$scope . planLoading = false ;
2013-10-02 04:48:03 +00:00
} ) ;
2013-10-02 06:05:53 +00:00
$scope . planChanging = false ;
2013-10-02 04:48:03 +00:00
$scope . subscribe = function ( planId ) {
2013-10-28 21:08:26 +00:00
PlanService . showSubscribeDialog ( $scope , planId , function ( ) {
// Subscribing.
$scope . planChanging = true ;
} , function ( plan ) {
// Subscribed.
subscribedToPlan ( plan ) ;
} , function ( ) {
// Failure.
$scope . errorMessage = 'Unable to subscribe.' ;
$scope . planChanging = false ;
2013-10-02 04:48:03 +00:00
} ) ;
} ;
2013-10-02 06:05:53 +00:00
$scope . changeSubscription = function ( planId ) {
$scope . planChanging = true ;
$scope . errorMessage = undefined ;
var subscriptionDetails = {
plan : planId ,
} ;
var changeSubscriptionRequest = Restangular . one ( 'user/plan' ) ;
changeSubscriptionRequest . customPUT ( subscriptionDetails ) . then ( subscribedToPlan , function ( ) {
// Failure
$scope . errorMessage = 'Unable to change subscription.' ;
$scope . planChanging = false ;
} ) ;
} ;
$scope . cancelSubscription = function ( ) {
2013-10-04 18:35:51 +00:00
$scope . changeSubscription ( 'free' ) ;
2013-10-02 06:05:53 +00:00
} ;
2013-10-02 22:14:51 +00:00
// Show the subscribe dialog if a plan was requested.
2013-10-04 18:35:51 +00:00
var requested = $routeParams [ 'plan' ]
if ( requested !== undefined && requested !== 'free' ) {
if ( PlanService . getPlan ( requested ) !== undefined ) {
$scope . subscribe ( requested ) ;
}
2013-10-02 22:14:51 +00:00
}
2013-10-10 17:44:34 +00:00
$scope . updatingUser = false ;
$scope . changePasswordSuccess = false ;
$ ( '.form-change-pw' ) . popover ( ) ;
$scope . changePassword = function ( ) {
$ ( '.form-change-pw' ) . popover ( 'hide' ) ;
$scope . updatingUser = true ;
$scope . changePasswordSuccess = false ;
var changePasswordPost = Restangular . one ( 'user/' ) ;
changePasswordPost . customPUT ( $scope . user ) . then ( function ( ) {
$scope . updatingUser = false ;
$scope . changePasswordSuccess = true ;
2013-10-10 18:02:28 +00:00
// Reset the form
$scope . user . password = '' ;
$scope . user . repeatPassword = '' ;
$scope . changePasswordForm . $setPristine ( ) ;
2013-10-10 17:44:34 +00:00
UserService . load ( ) ;
} , function ( result ) {
$scope . updatingUser = false ;
$scope . changePasswordError = result . data . message ;
$timeout ( function ( ) {
$ ( '.form-change-pw' ) . popover ( 'show' ) ;
} ) ;
} ) ;
} ;
2013-10-17 21:45:08 +00:00
}
2013-10-19 02:28:46 +00:00
function ImageViewCtrl ( $scope , $routeParams , $rootScope , Restangular ) {
var namespace = $routeParams . namespace ;
var name = $routeParams . name ;
var imageid = $routeParams . image ;
$ ( '#copyClipboard' ) . clipboardCopy ( ) ;
$scope . getMarkedDown = function ( string ) {
if ( ! string ) { return '' ; }
return getMarkedDown ( string ) ;
} ;
$scope . parseDate = function ( dateString ) {
return Date . parse ( dateString ) ;
} ;
$scope . getFolder = function ( filepath ) {
var index = filepath . lastIndexOf ( '/' ) ;
if ( index < 0 ) {
return '' ;
}
return filepath . substr ( 0 , index + 1 ) ;
} ;
$scope . getFolders = function ( filepath ) {
var index = filepath . lastIndexOf ( '/' ) ;
if ( index < 0 ) {
return '' ;
}
return filepath . substr ( 0 , index ) . split ( '/' ) ;
} ;
$scope . getFilename = function ( filepath ) {
var index = filepath . lastIndexOf ( '/' ) ;
if ( index < 0 ) {
return filepath ;
}
return filepath . substr ( index + 1 ) ;
} ;
$scope . setFolderFilter = function ( folderPath , index ) {
var parts = folderPath . split ( '/' ) ;
parts = parts . slice ( 0 , index + 1 ) ;
$scope . setFilter ( parts . join ( '/' ) ) ;
} ;
$scope . setFilter = function ( filter ) {
$scope . search = { } ;
$scope . search [ '$' ] = filter ;
document . getElementById ( 'change-filter' ) . value = filter ;
} ;
2013-10-19 23:46:30 +00:00
$scope . initializeTree = function ( ) {
if ( $scope . tree ) { return ; }
$scope . tree = new ImageFileChangeTree ( $scope . image , $scope . combinedChanges ) ;
setTimeout ( function ( ) {
$scope . tree . draw ( 'changes-tree-container' ) ;
} , 10 ) ;
} ;
2013-10-19 02:28:46 +00:00
// Fetch the image.
var imageFetch = Restangular . one ( 'repository/' + namespace + '/' + name + '/image/' + imageid ) ;
imageFetch . get ( ) . then ( function ( image ) {
$scope . loading = false ;
$scope . repo = {
'name' : name ,
'namespace' : namespace
} ;
$scope . image = image ;
$rootScope . title = 'View Image - ' + image . id ;
} , function ( ) {
$rootScope . title = 'Unknown Image' ;
$scope . loading = false ;
} ) ;
// Fetch the image changes.
var changesFetch = Restangular . one ( 'repository/' + namespace + '/' + name + '/image/' + imageid + '/changes' ) ;
changesFetch . get ( ) . then ( function ( changes ) {
var combinedChanges = [ ] ;
var addCombinedChanges = function ( c , kind ) {
for ( var i = 0 ; i < c . length ; ++ i ) {
combinedChanges . push ( {
'kind' : kind ,
'file' : c [ i ]
} ) ;
}
} ;
addCombinedChanges ( changes . added , 'added' ) ;
addCombinedChanges ( changes . removed , 'removed' ) ;
addCombinedChanges ( changes . changed , 'changed' ) ;
$scope . combinedChanges = combinedChanges ;
$scope . imageChanges = changes ;
} ) ;
}
2013-10-28 17:22:18 +00:00
function V1Ctrl ( $scope , $location , UserService ) {
2013-10-17 21:45:08 +00:00
$scope . $watch ( function ( ) { return UserService . currentUser ( ) ; } , function ( currentUser ) {
$scope . user = currentUser ;
} , true ) ;
2013-10-24 21:41:55 +00:00
}
2013-10-28 21:08:26 +00:00
function NewRepoCtrl ( $scope , $location , $http , UserService , Restangular , PlanService ) {
2013-10-30 19:44:01 +00:00
$scope . $watch ( function ( ) { return UserService . currentUser ( ) ; } , function ( currentUser ) {
$scope . user = currentUser ;
} , true ) ;
2013-10-24 21:41:55 +00:00
$scope . repo = {
'is_public' : 1 ,
'description' : '' ,
'initialize' : false
} ;
2013-10-28 17:09:22 +00:00
$ ( '#couldnotbuildModal' ) . on ( 'hidden.bs.modal' , function ( ) {
$scope . $apply ( function ( ) {
$location . path ( '/repository/' + $scope . created . namespace + '/' + $scope . created . name ) ;
} ) ;
} ) ;
2013-10-26 21:20:59 +00:00
var startBuild = function ( repo , fileId ) {
$scope . building = true ;
var data = {
'file_id' : fileId
} ;
var startBuildCall = Restangular . one ( 'repository/' + repo . namespace + '/' + repo . name + '/build/' ) ;
startBuildCall . customPOST ( data ) . then ( function ( resp ) {
$location . path ( '/repository/' + repo . namespace + '/' + repo . name ) ;
} , function ( ) {
$ ( '#couldnotbuildModal' ) . modal ( ) ;
} ) ;
} ;
var conductUpload = function ( repo , file , url , fileId , mimeType ) {
var request = new XMLHttpRequest ( ) ;
request . open ( 'PUT' , url , true ) ;
2013-10-26 22:37:53 +00:00
request . setRequestHeader ( 'Content-Type' , mimeType ) ;
2013-10-26 21:20:59 +00:00
request . onprogress = function ( e ) {
2013-10-28 17:09:22 +00:00
$scope . $apply ( function ( ) {
2013-10-26 22:37:53 +00:00
var percentLoaded ;
if ( e . lengthComputable ) {
$scope . upload _progress = ( e . loaded / e . total ) * 100 ;
}
} ) ;
2013-10-26 21:20:59 +00:00
} ;
request . onerror = function ( ) {
2013-10-28 17:09:22 +00:00
$scope . $apply ( function ( ) {
2013-10-26 22:37:53 +00:00
$ ( '#couldnotbuildModal' ) . modal ( ) ;
} ) ;
2013-10-26 21:20:59 +00:00
} ;
request . onreadystatechange = function ( ) {
var state = request . readyState ;
if ( state == 4 ) {
$scope . $apply ( function ( ) {
$scope . uploading = false ;
startBuild ( repo , fileId ) ;
} ) ;
return ;
}
} ;
request . send ( file ) ;
} ;
var startFileUpload = function ( repo ) {
$scope . uploading = true ;
$scope . uploading _progress = 0 ;
var uploader = $ ( '#file-drop' ) [ 0 ] ;
var file = uploader . files [ 0 ] ;
$scope . upload _file = file . name ;
var mimeType = file . type || 'application/octet-stream' ;
var data = {
'mimeType' : mimeType
} ;
2013-10-17 21:45:08 +00:00
2013-10-26 21:20:59 +00:00
var getUploadUrl = Restangular . one ( 'filedrop/' ) ;
getUploadUrl . customPOST ( data ) . then ( function ( resp ) {
conductUpload ( repo , file , resp . url , resp . file _id , mimeType ) ;
} , function ( ) {
$ ( '#couldnotbuildModal' ) . modal ( ) ;
} ) ;
2013-10-17 21:45:08 +00:00
} ;
2013-10-26 21:20:59 +00:00
2013-10-28 21:08:26 +00:00
var subscribedToPlan = function ( sub ) {
$scope . planChanging = false ;
$scope . subscription = sub ;
$scope . subscribedPlan = PlanService . getPlan ( sub . plan ) ;
$scope . planRequired = null ;
if ( $scope . subscription . usedPrivateRepos >= $scope . subscribedPlan . privateRepos ) {
$scope . planRequired = PlanService . getMinimumPlan ( $scope . subscription . usedPrivateRepos ) ;
}
} ;
2013-10-24 21:41:55 +00:00
$scope . editDescription = function ( ) {
if ( ! $scope . markdownDescriptionEditor ) {
var converter = Markdown . getSanitizingConverter ( ) ;
var editor = new Markdown . Editor ( converter , '-description' ) ;
editor . run ( ) ;
$scope . markdownDescriptionEditor = editor ;
}
$ ( '#wmd-input-description' ) [ 0 ] . value = $scope . repo . description ;
$ ( '#editModal' ) . modal ( { } ) ;
} ;
$scope . getMarkedDown = function ( string ) {
if ( ! string ) { return '' ; }
return getMarkedDown ( string ) ;
} ;
$scope . saveDescription = function ( ) {
$ ( '#editModal' ) . modal ( 'hide' ) ;
$scope . repo . description = $ ( '#wmd-input-description' ) [ 0 ] . value ;
} ;
2013-10-26 20:03:11 +00:00
$scope . createNewRepo = function ( ) {
2013-10-26 21:20:59 +00:00
var uploader = $ ( '#file-drop' ) [ 0 ] ;
if ( $scope . repo . initialize && uploader . files . length < 1 ) {
$ ( '#missingfileModal' ) . modal ( ) ;
return ;
}
2013-10-26 20:03:11 +00:00
$scope . creating = true ;
var repo = $scope . repo ;
var data = {
'repository' : repo . name ,
2013-10-30 00:21:18 +00:00
'visibility' : repo . is _public == '1' ? 'public' : 'private' ,
'description' : repo . description
2013-10-26 20:03:11 +00:00
} ;
var createPost = Restangular . one ( 'repository' ) ;
createPost . customPOST ( data ) . then ( function ( created ) {
2013-10-26 21:20:59 +00:00
$scope . creating = false ;
2013-10-28 17:09:22 +00:00
$scope . created = created ;
2013-10-26 21:20:59 +00:00
2013-10-26 20:03:11 +00:00
// Repository created. Start the upload process if applicable.
2013-10-26 21:20:59 +00:00
if ( $scope . repo . initialize ) {
startFileUpload ( created ) ;
return ;
}
2013-10-26 20:03:11 +00:00
// Otherwise, redirect to the repo page.
$location . path ( '/repository/' + created . namespace + '/' + created . name ) ;
} , function ( ) {
$ ( '#cannotcreateModal' ) . modal ( ) ;
$scope . creating = false ;
} ) ;
} ;
2013-10-28 21:08:26 +00:00
$scope . upgradePlan = function ( ) {
PlanService . showSubscribeDialog ( $scope , $scope . planRequired . stripeId , function ( ) {
// Subscribing.
$scope . planChanging = true ;
} , function ( plan ) {
// Subscribed.
subscribedToPlan ( plan ) ;
} , function ( ) {
// Failure.
$ ( '#couldnotsubscribeModal' ) . modal ( ) ;
$scope . planChanging = false ;
} ) ;
} ;
$scope . plans = PlanService . planList ( ) ;
// Load the user's subscription information in case they want to create a private
// repository.
var getSubscription = Restangular . one ( 'user/plan' ) ;
getSubscription . get ( ) . then ( subscribedToPlan , function ( ) {
// User has no subscription
$scope . planRequired = PlanService . getMinimumPlan ( 1 ) ;
} ) ;
2013-09-27 00:34:58 +00:00
}