Delete the old UI!

This commit is contained in:
Joseph Schorr 2015-06-29 12:33:00 +03:00
parent 33039e9bc4
commit ff3d8bb013
54 changed files with 308 additions and 4383 deletions

View file

@ -1,3 +1,42 @@
<span class="entity-reference-element">
<span quay-include="{'Config.isNewLayout()': 'directives/new-entity-reference.html', '!Config.isNewLayout()': 'directives/old-entity-reference.html'}"></span>
<span class="new-entity-reference" data-title="{{ getTitle(entity) }} {{ entity.name }}" bs-tooltip>
<span ng-switch on="entity.kind">
<!-- Team -->
<span ng-switch-when="team">
<span class="avatar" data="entity.avatar" size="avatarSize || 16"></span>
<span class="entity-name anchor"
href="/organization/{{ namespace }}/teams/{{ entity.name }}"
is-only-text="!getIsAdmin(namespace)">
{{ entity.name }}
</span>
</span>
<!-- Organization -->
<span ng-switch-when="org">
<span class="avatar" size="avatarSize || 16" data="entity.avatar"></span>
<span class="entity-name anchor" href="/organization/{{ entity.name }}"
is-only-text="!getIsAdmin(entity.name)">
</span>
</span>
<!-- User or Robot -->
<span ng-switch-when="user">
<!-- User -->
<span ng-if="!entity.is_robot">
<span class="avatar" size="avatarSize || 16" data="entity.avatar"></span>
<a class="entity-name" href="/user/{{ entity.name }}">{{ entity.name }}</a>
</span>
<!-- Robot -->
<span ng-if="entity.is_robot">
<i class="fa ci-robot fa-lg"></i>
<span class="entity-name anchor" href="{{ getRobotUrl(entity.name) }}"
is-only-text="!getIsAdmin(getPrefix(entity.name))">
<span class="prefix">{{ getPrefix(entity.name) }}+</span>
<span>{{ getShortenedName(entity.name) }}</span>
</span>
</span>
</span>
</span>
</span>
</span>

View file

@ -55,12 +55,7 @@
<li class="menuitem" role="presentation" ng-repeat="team in teams | orderBy: 'name'" ng-show="!lazyLoading"
ng-click="setEntity(team.name, 'team', false, team.avatar)">
<a role="menuitem" tabindex="-1" href="javascript:void(0)">
<span ng-if="!Config.isNewLayout()">
<i class="fa fa-group"></i> <span>{{ team.name }}</span>
</span>
<span ng-if="Config.isNewLayout()">
<span class="avatar" data="team.avatar" size="16"></span> <span>{{ team.name }}</span>
</span>
<span class="avatar" data="team.avatar" size="16"></span> <span>{{ team.name }}</span>
</a>
</li>

View file

@ -1,3 +1,207 @@
<span class="header-bar-parent">
<span quay-include="{'Config.isNewLayout()': 'directives/new-header-bar.html', '!Config.isNewLayout()': 'directives/old-header-bar.html'}"></span>
<div class="header-bar-element">
<div class="header-bar-content" ng-class="searchVisible ? 'search-visible' : ''">
<!-- Quay -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
&equiv;
</button>
<a class="navbar-brand" ng-href="{{ user.anonymous ? '/' : '/repository/' }}" target="{{ appLinkTarget() }}">
<span id="quay-logo" ng-style="{'background-image': 'url(' + getEnterpriseLogo() + ')'}"></span>
</a>
<span class="user-tools visible-xs" style="float: right;">
<i class="fa fa-search fa-lg user-tool" ng-click="toggleSearch()"
data-placement="bottom" data-title="Search" bs-tooltip
ng-if="searchingAllowed"></i>
</span>
</div>
<!-- Collapsable stuff -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<!-- Not signed in -->
<ul class="nav navbar-nav navbar-links" ng-if="user.anonymous">
<li><a ng-href="/tour/" target="{{ appLinkTarget() }}" quay-section="tour">Tour</a></li>
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
<li quay-require="['BILLING']"><a ng-href="/plans/" target="{{ appLinkTarget() }}" quay-section="plans">Pricing</a></li>
<li><a href="http://docs.quay.io/" target="_blank">Docs</a></li>
</ul>
<!-- Signed in -->
<ul class="nav navbar-nav navbar-links" ng-if="!user.anonymous">
<li><a ng-href="/repository/" target="{{ appLinkTarget() }}" quay-section="repository">Repositories</a></li>
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
<li><a href="http://docs.quay.io/" target="_blank">Docs</a></li>
</ul>
<!-- Phone -->
<ul class="nav navbar-nav navbar-right visible-xs" ng-switch on="user.anonymous">
<li ng-switch-when="false">
<a href="/user/{{ user.username }}?tab=settings" class="user-view" target="{{ appLinkTarget() }}">
<span class="avatar" size="32" data="user.avatar"></span>
{{ user.username }}
</a>
</li>
<li ng-switch-default>
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
</li>
</ul>
<!-- Normal -->
<ul class="nav navbar-nav navbar-right hidden-xs" ng-switch on="user.anonymous">
<li>
<span class="navbar-left user-tools">
<i class="fa fa-search fa-lg user-tool" ng-click="toggleSearch()"
data-placement="bottom" data-title="Search - Keyboard Shortcut: /" bs-tooltip
ng-if="searchingAllowed"></i>
</span>
</li>
<li>
<span class="navbar-left user-tools with-menu" ng-show="!user.anonymous">
<span class="dropdown">
<a href="javascript:void(0)" class="dropdown-toggle new-menu" data-toggle="dropdown">
<i class="fa fa-plus user-tool"
data-placement="bottom" data-title="Create New..." bs-tooltip></i>
<b class="caret"></b>
</a>
<ul class="dropdown-menu context-dropdown">
<li>
<a href="/organizations/new">
<span class="avatar" size="16" data="{'name': '+', color: '#ccc'}"></span>
New Organization
</a>
</li>
<li>
<a href="/new{{ getNamespace(currentPageContext) ? '?namespace=' + getNamespace(currentPageContext) : '' }}">
<i class="fa fa-hdd-o"></i> New Repository
</a>
</li>
<li role="presentation" class="divider" ng-if="getNamespace(currentPageContext) && canAdmin(getNamespace(currentPageContext))"></li>
<li role="presentation" class="dropdown-header"
ng-if="getNamespace(currentPageContext) && canAdmin(getNamespace(currentPageContext))">
Namespace {{ getNamespace(currentPageContext) }}
</li>
<li ng-if="isOrganization(getNamespace(currentPageContext)) && canAdmin(getNamespace(currentPageContext))">
<a href="javascript:void(0)" ng-click="createTeam(currentPageContext)">
<i class="fa fa-group"></i> New Team
</a>
</li>
<li ng-if="canAdmin(getNamespace(currentPageContext))">
<a href="javascript:void(0)" ng-click="createRobot(currentPageContext)">
<i class="fa ci-robot"></i> New Robot Account
</a>
</li>
<li role="presentation" class="divider" ng-if="currentPageContext.repository && currentPageContext.repository.can_write"></li>
<li role="presentation" class="dropdown-header"
ng-if="currentPageContext.repository && currentPageContext.repository.can_write">
Repository {{ currentPageContext.repository.namespace }}/{{ currentPageContext.repository.name }}
</li>
<li ng-if="currentPageContext.repository && currentPageContext.repository.can_write">
<a href="javascript:void(0)" ng-click="startBuild()">
<i class="fa fa-tasks"></i> New Dockerfile Build
</a>
</li>
</ul>
</span>
</span>
</li>
<li>
<span class="navbar-left user-tools" ng-show="!user.anonymous">
<a href="javascript:void(0)" data-template="/static/directives/notification-bar.html"
data-container="body" data-animation="am-slide-right" bs-aside>
<i class="fa fa-bell user-tool"
data-placement="bottom" data-title="Notifications" bs-tooltip></i>
<span class="notifications-bubble"></span>
</a>
</span>
</li>
<li class="dropdown" ng-switch-when="false">
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown user-view" data-toggle="dropdown">
<span class="avatar" size="32" data="user.avatar"></span>
{{ user.username }}
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li>
<a href="/user/{{ user.username }}?tab=settings" target="{{ appLinkTarget() }}">
Account Settings
</a>
</li>
<li ng-if="user.super_user"><a href="/superuser/"><strong>Super User Admin Panel</strong></a></li>
<li><a href="javascript:void(0)" ng-click="signout()">Sign out</a></li>
</ul>
</li>
<li ng-switch-default>
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div>
<div class="search-box" ng-class="getSearchBoxClasses(searchVisible, searchResultState)">
<div class="search-label">Search For</div>
<div class="search-box-wrapper">
<input id="search-box-input" type="search" placeholder="(Enter Search Terms)"
ng-model-options="{'debounce': 250}" ng-model="currentSearchQuery"
debounce="250"
ng-keydown="handleSearchKeyDown($event)">
</div>
</div>
<div class="search-results"
ng-class="searchVisible && searchResultState ? searchResultState.state : ''"
ng-class="{'height': (searchResultState.results.length * 40) + 28}">
<div class="cor-loader" ng-if="searchResultState.state == 'loading'"></div>
<div ng-if="searchResultState.state == 'no-results'">No matching results found</div>
<ul ng-if="searchResultState.state == 'results'">
<li ng-repeat="result in searchResultState.results" ng-mouseover="setCurrentResult($index)"
ng-class="searchResultState.current == $index ? 'current' : ''"
ng-click="showResult(result)">
<span class="kind">{{ result.kind }}</span>
<span class="score" style="display: none">{{ result.score.toString().substr(0, 4) }}</span>
<span ng-switch on="result.kind">
<!-- Team -->
<span ng-switch-when="team">
<strong>
<span class="avatar" data="result.avatar" size="16"></span>
<span class="result-name">{{ result.name }}</span>
</strong>
<span class="clarification">
under organization
<span class="avatar" data="result.organization.avatar" size="16"></span>
<span class="result-name">{{ result.organization.name }}</span>
</span>
</span>
<span ng-switch-when="user">
<span class="avatar" data="result.avatar" size="16"></span>
<span class="result-name">{{ result.name }}</span>
</span>
<span ng-switch-when="organization">
<span class="avatar" data="result.avatar" size="16"></span>
<span class="result-name">{{ result.name }}</span>
</span>
<span href="/user/{{ result.name }}" ng-switch-when="robot">
<i class="fa ci-robot"></i>
<span class="result-name">{{ result.name }}</span>
</span>
<span ng-switch-when="repository">
<span class="avatar" data="result.namespace.avatar" size="16"></span>
<span class="result-name">{{ result.namespace.name }}/{{ result.name }}</span>
<div class="result-description" ng-if="result.description">
<div class="description markdown-view" content="result.description"
first-line-only="true" placeholder-needed="false"></div>
</div>
</span>
</span>
</li>
</ul>
</div>
<div class="dockerfile-build-dialog"
show-now="showBuildDialogCounter"
repository="currentPageContext.repository"
build-started="handleBuildStarted(build, currentPageContext)">
</div>
</div>
</span>

View file

@ -1,40 +0,0 @@
<span class="new-entity-reference" data-title="{{ getTitle(entity) }} {{ entity.name }}" bs-tooltip>
<span ng-switch on="entity.kind">
<!-- Team -->
<span ng-switch-when="team">
<span class="avatar" data="entity.avatar" size="avatarSize || 16"></span>
<span class="entity-name anchor"
href="/organization/{{ namespace }}/teams/{{ entity.name }}"
is-only-text="!getIsAdmin(namespace)">
{{ entity.name }}
</span>
</span>
<!-- Organization -->
<span ng-switch-when="org">
<span class="avatar" size="avatarSize || 16" data="entity.avatar"></span>
<span class="entity-name anchor" href="/organization/{{ entity.name }}"
is-only-text="!getIsAdmin(entity.name)">
</span>
</span>
<!-- User or Robot -->
<span ng-switch-when="user">
<!-- User -->
<span ng-if="!entity.is_robot">
<span class="avatar" size="avatarSize || 16" data="entity.avatar"></span>
<a class="entity-name" href="/user/{{ entity.name }}">{{ entity.name }}</a>
</span>
<!-- Robot -->
<span ng-if="entity.is_robot">
<i class="fa ci-robot fa-lg"></i>
<span class="entity-name anchor" href="{{ getRobotUrl(entity.name) }}"
is-only-text="!getIsAdmin(getPrefix(entity.name))">
<span class="prefix">{{ getPrefix(entity.name) }}+</span>
<span>{{ getShortenedName(entity.name) }}</span>
</span>
</span>
</span>
</span>
</span>

View file

@ -1,205 +0,0 @@
<div class="header-bar-element">
<div class="header-bar-content" ng-class="searchVisible ? 'search-visible' : ''">
<!-- Quay -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
&equiv;
</button>
<a class="navbar-brand" ng-href="{{ user.anonymous ? '/' : '/repository/' }}" target="{{ appLinkTarget() }}">
<span id="quay-logo" ng-style="{'background-image': 'url(' + getEnterpriseLogo() + ')'}"></span>
</a>
<span class="user-tools visible-xs" style="float: right;">
<i class="fa fa-search fa-lg user-tool" ng-click="toggleSearch()"
data-placement="bottom" data-title="Search" bs-tooltip
ng-if="searchingAllowed"></i>
</span>
</div>
<!-- Collapsable stuff -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<!-- Not signed in -->
<ul class="nav navbar-nav navbar-links" ng-if="user.anonymous">
<li><a ng-href="/tour/" target="{{ appLinkTarget() }}" quay-section="tour">Tour</a></li>
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
<li quay-require="['BILLING']"><a ng-href="/plans/" target="{{ appLinkTarget() }}" quay-section="plans">Pricing</a></li>
<li><a href="http://docs.quay.io/" target="_blank">Docs</a></li>
</ul>
<!-- Signed in -->
<ul class="nav navbar-nav navbar-links" ng-if="!user.anonymous">
<li><a ng-href="/repository/" target="{{ appLinkTarget() }}" quay-section="repository">Repositories</a></li>
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
<li><a href="http://docs.quay.io/" target="_blank">Docs</a></li>
</ul>
<!-- Phone -->
<ul class="nav navbar-nav navbar-right visible-xs" ng-switch on="user.anonymous">
<li ng-switch-when="false">
<a href="/user/{{ user.username }}?tab=settings" class="user-view" target="{{ appLinkTarget() }}">
<span class="avatar" size="32" data="user.avatar"></span>
{{ user.username }}
</a>
</li>
<li ng-switch-default>
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
</li>
</ul>
<!-- Normal -->
<ul class="nav navbar-nav navbar-right hidden-xs" ng-switch on="user.anonymous">
<li>
<span class="navbar-left user-tools">
<i class="fa fa-search fa-lg user-tool" ng-click="toggleSearch()"
data-placement="bottom" data-title="Search - Keyboard Shortcut: /" bs-tooltip
ng-if="searchingAllowed"></i>
</span>
</li>
<li>
<span class="navbar-left user-tools with-menu" ng-show="!user.anonymous">
<span class="dropdown">
<a href="javascript:void(0)" class="dropdown-toggle new-menu" data-toggle="dropdown">
<i class="fa fa-plus user-tool"
data-placement="bottom" data-title="Create New..." bs-tooltip></i>
<b class="caret"></b>
</a>
<ul class="dropdown-menu context-dropdown">
<li>
<a href="/organizations/new">
<span class="avatar" size="16" data="{'name': '+', color: '#ccc'}"></span>
New Organization
</a>
</li>
<li>
<a href="/new{{ getNamespace(currentPageContext) ? '?namespace=' + getNamespace(currentPageContext) : '' }}">
<i class="fa fa-hdd-o"></i> New Repository
</a>
</li>
<li role="presentation" class="divider" ng-if="getNamespace(currentPageContext) && canAdmin(getNamespace(currentPageContext))"></li>
<li role="presentation" class="dropdown-header"
ng-if="getNamespace(currentPageContext) && canAdmin(getNamespace(currentPageContext))">
Namespace {{ getNamespace(currentPageContext) }}
</li>
<li ng-if="isOrganization(getNamespace(currentPageContext)) && canAdmin(getNamespace(currentPageContext))">
<a href="javascript:void(0)" ng-click="createTeam(currentPageContext)">
<i class="fa fa-group"></i> New Team
</a>
</li>
<li ng-if="canAdmin(getNamespace(currentPageContext))">
<a href="javascript:void(0)" ng-click="createRobot(currentPageContext)">
<i class="fa ci-robot"></i> New Robot Account
</a>
</li>
<li role="presentation" class="divider" ng-if="currentPageContext.repository && currentPageContext.repository.can_write"></li>
<li role="presentation" class="dropdown-header"
ng-if="currentPageContext.repository && currentPageContext.repository.can_write">
Repository {{ currentPageContext.repository.namespace }}/{{ currentPageContext.repository.name }}
</li>
<li ng-if="currentPageContext.repository && currentPageContext.repository.can_write">
<a href="javascript:void(0)" ng-click="startBuild()">
<i class="fa fa-tasks"></i> New Dockerfile Build
</a>
</li>
</ul>
</span>
</span>
</li>
<li>
<span class="navbar-left user-tools" ng-show="!user.anonymous">
<a href="javascript:void(0)" data-template="/static/directives/notification-bar.html"
data-container="body" data-animation="am-slide-right" bs-aside>
<i class="fa fa-bell user-tool"
data-placement="bottom" data-title="Notifications" bs-tooltip></i>
<span class="notifications-bubble"></span>
</a>
</span>
</li>
<li class="dropdown" ng-switch-when="false">
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown user-view" data-toggle="dropdown">
<span class="avatar" size="32" data="user.avatar"></span>
{{ user.username }}
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li>
<a href="/user/{{ user.username }}?tab=settings" target="{{ appLinkTarget() }}">
Account Settings
</a>
</li>
<li ng-if="user.super_user"><a href="/superuser/"><strong>Super User Admin Panel</strong></a></li>
<li><a href="javascript:void(0)" ng-click="signout()">Sign out</a></li>
</ul>
</li>
<li ng-switch-default>
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div>
<div class="search-box" ng-class="getSearchBoxClasses(searchVisible, searchResultState)">
<div class="search-label">Search For</div>
<div class="search-box-wrapper">
<input id="search-box-input" type="search" placeholder="(Enter Search Terms)"
ng-model-options="{'debounce': 250}" ng-model="currentSearchQuery"
debounce="250"
ng-keydown="handleSearchKeyDown($event)">
</div>
</div>
<div class="search-results"
ng-class="searchVisible && searchResultState ? searchResultState.state : ''"
ng-class="{'height': (searchResultState.results.length * 40) + 28}">
<div class="cor-loader" ng-if="searchResultState.state == 'loading'"></div>
<div ng-if="searchResultState.state == 'no-results'">No matching results found</div>
<ul ng-if="searchResultState.state == 'results'">
<li ng-repeat="result in searchResultState.results" ng-mouseover="setCurrentResult($index)"
ng-class="searchResultState.current == $index ? 'current' : ''"
ng-click="showResult(result)">
<span class="kind">{{ result.kind }}</span>
<span class="score" style="display: none">{{ result.score.toString().substr(0, 4) }}</span>
<span ng-switch on="result.kind">
<!-- Team -->
<span ng-switch-when="team">
<strong>
<span class="avatar" data="result.avatar" size="16"></span>
<span class="result-name">{{ result.name }}</span>
</strong>
<span class="clarification">
under organization
<span class="avatar" data="result.organization.avatar" size="16"></span>
<span class="result-name">{{ result.organization.name }}</span>
</span>
</span>
<span ng-switch-when="user">
<span class="avatar" data="result.avatar" size="16"></span>
<span class="result-name">{{ result.name }}</span>
</span>
<span ng-switch-when="organization">
<span class="avatar" data="result.avatar" size="16"></span>
<span class="result-name">{{ result.name }}</span>
</span>
<span href="/user/{{ result.name }}" ng-switch-when="robot">
<i class="fa ci-robot"></i>
<span class="result-name">{{ result.name }}</span>
</span>
<span ng-switch-when="repository">
<span class="avatar" data="result.namespace.avatar" size="16"></span>
<span class="result-name">{{ result.namespace.name }}/{{ result.name }}</span>
<div class="result-description" ng-if="result.description">
<div class="description markdown-view" content="result.description"
first-line-only="true" placeholder-needed="false"></div>
</div>
</span>
</span>
</li>
</ul>
</div>
<div class="dockerfile-build-dialog"
show-now="showBuildDialogCounter"
repository="currentPageContext.repository"
build-started="handleBuildStarted(build, currentPageContext)">
</div>
</div>

View file

@ -1,17 +0,0 @@
<div class="new-role-group btn-group btn-group-sm">
<div class="dropdown" style="text-align: left;">
<button class="btn btn-default" ng-class="getRoleInfo(currentRole).kind" data-toggle="dropdown"
ng-disabled="readOnly">
<span ng-if="currentRole" class="role-title">{{ getRoleInfo(currentRole).title }}</span>
<span ng-if="!currentRole">(Select)</span>
<span class="caret" ng-if="!readOnly"></span>
</button>
<ul class="dropdown-menu" ng-class="pullLeft == 'true' ? '' : 'pull-right'" ng-if="!readOnly">
<li ng-repeat="roleInfo in fullRoles" ng-class="roleInfo.kind">
<a href="javascript:void(0)" ng-click="setRole(roleInfo.id)">{{ roleInfo.title }}
<div class="role-help-text">{{ roleInfo.description }}</div>
</a>
</li>
</ul>
</div>
</div>

View file

@ -1,39 +0,0 @@
<!-- DEPRECATED! -->
<span class="old-entity-reference">
<span ng-if="entity.kind == 'team'">
<i class="fa fa-group" data-title="Team" bs-tooltip="tooltip.title" data-container="body"></i>
<span class="entity-name">
<span ng-if="!getIsAdmin(namespace)">{{entity.name}}</span>
<span ng-if="getIsAdmin(namespace)"><a href="/organization/{{ namespace }}/teams/{{ entity.name }}">{{entity.name}}</a></span>
</span>
</span>
<span ng-if="entity.kind == 'org'">
<span class="avatar" size="avatarSize || 16" data="entity.avatar"></span>
<span class="entity-name">
<span ng-if="!getIsAdmin(entity.name)">{{entity.name}}</span>
<span ng-if="getIsAdmin(entity.name)"><a href="/organization/{{ entity.name }}">{{entity.name}}</a></span>
</span>
</span>
<span ng-if="entity.kind != 'team' && entity.kind != 'org'">
<span class="avatar" size="avatarSize || 16" data="entity.avatar" ng-if="showAvatar == 'true' && entity.avatar"></span>
<span ng-if="showAvatar != 'true' || !entity.avatar">
<i class="fa fa-user" ng-show="!entity.is_robot" data-title="User" bs-tooltip="tooltip.title" data-container="body"></i>
<i class="fa ci-robot" ng-show="entity.is_robot" data-title="Robot Account" bs-tooltip="tooltip.title" data-container="body"></i>
</span>
<span class="entity-name" ng-if="entity.is_robot">
<a href="{{ getRobotUrl(entity.name) }}" ng-if="getIsAdmin(getPrefix(entity.name))">
<span class="prefix">{{ getPrefix(entity.name) }}+</span><span>{{ getShortenedName(entity.name) }}</span>
</a>
<span ng-if="!getIsAdmin(getPrefix(entity.name))">
<span class="prefix">{{ getPrefix(entity.name) }}+</span><span>{{ getShortenedName(entity.name) }}</span>
</span>
</span>
<span class="entity-name" ng-if="!entity.is_robot">
<span>{{getShortenedName(entity.name)}}</span>
</span>
</span>
<i class="fa fa-exclamation-triangle" ng-if="entity.is_org_member === false"
data-title="This user is not a member of the organization" bs-tooltip="tooltip.title" data-container="body">
</i>
</span>

View file

@ -1,78 +0,0 @@
<!-- Quay -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse" style="padding: 0px; padding-left: 4px; padding-right: 4px;">
&equiv;
</button>
<a class="navbar-brand" href="/" target="{{ appLinkTarget() }}">
<span id="quay-logo" ng-style="{'background-image': 'url(' + getEnterpriseLogo() + ')'}"></span>
</a>
</div>
<!-- Collapsable stuff -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav navbar-links">
<li><a ng-href="/tour/" target="{{ appLinkTarget() }}" quay-section="tour">Tour</a></li>
<li><a ng-href="/repository/" target="{{ appLinkTarget() }}" quay-section="repository">Repositories</a></li>
<li><a href="http://docs.quay.io/" target="_blank">Docs</a></li>
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
<li quay-require="['BILLING']"><a ng-href="/plans/" target="{{ appLinkTarget() }}" quay-section="plans">Pricing</a></li>
<li><a ng-href="{{ user.organizations.length ? '/organizations/' : '/tour/organizations/' }}" target="{{ appLinkTarget() }}" quay-section="organization">Organizations</a></li>
</ul>
<!-- Phone -->
<ul class="nav navbar-nav navbar-right visible-xs" ng-switch on="user.anonymous">
<li ng-switch-when="false">
<a href="/user/" class="user-view" target="{{ appLinkTarget() }}">
<span class="avatar" size="32" data="user.avatar"></span>
{{ user.username }}
</a>
</li>
<li ng-switch-default>
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
</li>
</ul>
<!-- Normal -->
<ul class="nav navbar-nav navbar-right hidden-xs" ng-switch on="user.anonymous">
<li>
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<span class="repo-search"></span>
</div>
</form>
<span class="navbar-left user-tools" ng-show="!user.anonymous">
<a href="/new/"><i class="fa fa-upload user-tool" bs-tooltip="tooltip.title" data-placement="bottom" data-title="Create new repository" data-container="body"></i></a>
</span>
</li>
<li class="dropdown" ng-switch-when="false">
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown user-view" data-toggle="dropdown">
<span class="avatar" size="32" data="user.avatar"></span>
{{ user.username }}
<span class="notifications-bubble"></span>
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li>
<a href="/user/" target="{{ appLinkTarget() }}">
Account Settings
</a>
</li>
<li ng-if="notificationService.notifications.length">
<a href="javascript:void(0)" data-template="/static/directives/notification-bar.html"
data-animation="am-slide-right" bs-aside="aside" data-container="body">
Notifications
<span class="notifications-bubble"></span>
</a>
</li>
<li><a ng-href="/organizations/" target="{{ appLinkTarget() }}">Organizations</a></li>
<li ng-if="user.super_user"><a href="/superuser/"><strong>Super User Admin Panel</strong></a></li>
<li><a href="javascript:void(0)" ng-click="signout()">Sign out</a></li>
</ul>
</li>
<li ng-switch-default>
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
</li>
</ul>
</div><!-- /.navbar-collapse -->

View file

@ -1,6 +0,0 @@
<div class="btn-group btn-group-sm">
<button ng-repeat="role in fullRoles"
type="button" class="btn" ng-click="setRole(role.id)"
ng-class="(currentRole == role.id) ? ('active btn-' + role.kind) : 'btn-default'"
ng-disabled="readOnly">{{ role.title }}</button>
</div>

View file

@ -35,16 +35,16 @@
<table class="cor-table" ng-if="(robots | filter:robotFilter).length">
<thead>
<td class="caret-col hidden-xs" ng-if="(user || organization.is_admin) && Config.isNewLayout()"></td>
<td class="caret-col hidden-xs" ng-if="(user || organization.is_admin)"></td>
<td>Robot Account Name</td>
<td ng-if="organization && Config.isNewLayout()">Teams</td>
<td ng-if="Config.isNewLayout()">Direct Repository Permissions</td>
<td ng-if="organization">Teams</td>
<td>Direct Repository Permissions</td>
<td class="options-col"></td>
</thead>
<tbody ng-repeat="robotInfo in robots | filter:robotFilter | orderBy:getShortenedRobotName" bindonce>
<tr ng-class="robotInfo.showing_permissions ? 'open' : 'closed'">
<td class="caret-col hidden-xs" bo-if="(user || organization.is_admin) && Config.isNewLayout()">
<td class="caret-col hidden-xs" bo-if="(user || organization.is_admin)">
<span bo-if="robotInfo.repositories.length > 0" ng-click="showPermissions(robotInfo)">
<i class="fa"
ng-class="robotInfo.showing_permissions ? 'fa-caret-down' : 'fa-caret-right'"
@ -58,7 +58,7 @@
<span bo-text="getShortenedName(robotInfo.name)"></span>
</a>
</td>
<td bo-if="organization && Config.isNewLayout()">
<td bo-if="organization">
<span class="empty" bo-if="robotInfo.teams.length == 0">
(Not a member of any team)
</span>
@ -71,7 +71,7 @@
</span>
</span>
</td>
<td bo-if="Config.isNewLayout()">
<td>
<span class="empty" bo-if="robotInfo.repositories.length == 0">
(No direct permissions on any repositories)
</span>

View file

@ -1 +1,17 @@
<span quay-include="{'Config.isNewLayout()': 'directives/new-role-group.html', '!Config.isNewLayout()': 'directives/old-role-group.html'}"></span>
<div class="new-role-group btn-group btn-group-sm">
<div class="dropdown" style="text-align: left;">
<button class="btn btn-default" ng-class="getRoleInfo(currentRole).kind" data-toggle="dropdown"
ng-disabled="readOnly">
<span ng-if="currentRole" class="role-title">{{ getRoleInfo(currentRole).title }}</span>
<span ng-if="!currentRole">(Select)</span>
<span class="caret" ng-if="!readOnly"></span>
</button>
<ul class="dropdown-menu" ng-class="pullLeft == 'true' ? '' : 'pull-right'" ng-if="!readOnly">
<li ng-repeat="roleInfo in fullRoles" ng-class="roleInfo.kind">
<a href="javascript:void(0)" ng-click="setRole(roleInfo.id)">{{ roleInfo.title }}
<div class="role-help-text">{{ roleInfo.description }}</div>
</a>
</li>
</ul>
</div>
</div>

View file

@ -84,12 +84,6 @@ quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeP
// WARNING WARNING WARNING
var layoutProfile = 'layout';
// Check for the override flag.
if (window.location.search.indexOf('old-ui=1') >= 0) {
layoutProfile = 'old-layout';
}
window.console.log('Using layout profile: ' + layoutProfile);
var routeBuilder = new AngularRouteBuilder($routeProvider, pages, [
@ -108,18 +102,9 @@ quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeP
// Image View
.route('/repository/:namespace/:name/image/:image', 'image-view')
// Repo Admin
.route('/repository/:namespace/:name/admin', 'repo-admin')
// Repo Builds
.route('/repository/:namespace/:name/build', 'repo-build')
// Repo Build View
.route('/repository/:namespace/:name/build/:buildid', 'build-view')
// Repo Build Package
.route('/repository/:namespace/:name/build/:buildid/buildpack', 'build-package')
// Repo List
.route('/repository/', 'repo-list')
@ -132,24 +117,15 @@ quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeP
// View Organization
.route('/organization/:orgname', 'org-view')
// Organization Admin
.route('/organization/:orgname/admin', 'org-admin')
// View Organization Team
.route('/organization/:orgname/teams/:teamname', 'team-view')
// Organization Member Logs
.route('/organization/:orgname/logs/:membername', 'org-member-logs')
// Organization View Application
.route('/organization/:orgname/application/:clientid', 'manage-application')
// View User
.route('/user/:username', 'user-view')
// DEPRECATED: User Admin
.route('/user/', 'user-admin')
// Sign In
.route('/signin/', 'signin')
@ -189,9 +165,6 @@ quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeP
// Confirm Invite
.route('/confirminvite', 'confirm-invite')
// Enable/disable experimental layout
.route('/__exp/newlayout', 'exp-new-layout')
// Default: Redirect to the landing page
.otherwise({redirectTo: '/'});
}]);

View file

@ -235,21 +235,6 @@ angular.module("core-ui", [])
return directiveDefinitionObject;
})
// TODO(jschorr): Remove this once new layout is in prod.
.directive('corTabShim', function() {
var directiveDefinitionObject = {
priority: 3,
replace: false,
transclude: false,
restrict: 'C',
scope: {},
controller: function($rootScope, $scope, $element, $timeout, $location, UIService) {
UIService.initializeTabs($scope, $element);
}
};
return directiveDefinitionObject;
})
.directive('corTabs', function() {
var directiveDefinitionObject = {
priority: 3,

View file

@ -21,14 +21,6 @@ angular.module('quay').directive('entityReference', function () {
};
$scope.getRobotUrl = function(name) {
if (Config.isNewLayout()) {
return $scope.getNewRobotUrl(name);
} else {
return $scope.getOldRobotUrl(name);
}
};
$scope.getNewRobotUrl = function(name) {
var namespace = $scope.getPrefix(name);
if (!namespace) {
return '';
@ -47,26 +39,6 @@ angular.module('quay').directive('entityReference', function () {
return '/organization/' + org['name'] + '?tab=robots&showRobot=' + UtilService.textToSafeHtml(name);
};
// TODO(jschorr): Remove when new layout is in prod.
$scope.getOldRobotUrl = function(name) {
var namespace = $scope.getPrefix(name);
if (!namespace) {
return '';
}
if (!$scope.getIsAdmin(namespace)) {
return '';
}
var org = UserService.getOrganization(namespace);
if (!org) {
// This robot is owned by the user.
return '/user/?tab=robots&showRobot=' + UtilService.textToSafeHtml(name);
}
return '/organization/' + org['name'] + '/admin?tab=robots&showRobot=' + UtilService.textToSafeHtml(name);
};
$scope.getTitle = function(entity) {
if (!entity) { return ''; }

View file

@ -14,41 +14,36 @@ angular.module('quay').directive('headerBar', function () {
},
controller: function($rootScope, $scope, $element, $location, $timeout, hotkeys, UserService,
PlanService, ApiService, NotificationService, Config, CreateService, Features) {
$scope.isNewLayout = Config.isNewLayout();
var hotkeysAdded = false;
var userUpdated = function(cUser) {
$scope.searchingAllowed = Features.ANONYMOUS_ACCESS || !cUser.anonymous;
if (hotkeysAdded) { return; }
hotkeysAdded = true;
if ($scope.isNewLayout) {
hotkeysAdded = true;
// Register hotkeys.
if ($scope.searchingAllowed) {
hotkeys.add({
combo: '/',
description: 'Show search',
callback: function(e) {
e.preventDefault();
e.stopPropagation();
$scope.toggleSearch();
}
});
}
// Register hotkeys.
if ($scope.searchingAllowed) {
hotkeys.add({
combo: '/',
description: 'Show search',
callback: function(e) {
e.preventDefault();
e.stopPropagation();
$scope.toggleSearch();
}
});
}
if (!cUser.anonymous) {
hotkeys.add({
combo: 'alt+c',
description: 'Create new repository',
callback: function(e) {
e.preventDefault();
e.stopPropagation();
$location.url('/new');
}
});
}
if (!cUser.anonymous) {
hotkeys.add({
combo: 'alt+c',
description: 'Create new repository',
callback: function(e) {
e.preventDefault();
e.stopPropagation();
$location.url('/new');
}
});
}
};

View file

@ -1,143 +0,0 @@
(function() {
/**
* Page which displays a build package for a specific build.
*/
angular.module('quayPages').config(['pages', function(pages) {
pages.create('build-package', 'build-package.html', BuildPackageCtrl);
}]);
function BuildPackageCtrl($scope, Restangular, ApiService, DataFileService, $routeParams, $rootScope, $location, $timeout) {
var namespace = $routeParams.namespace;
var name = $routeParams.name;
var buildid = $routeParams.buildid;
var params = {
'repository': namespace + '/' + name,
'build_uuid': buildid
};
$scope.initializeTree = function() {
if ($scope.drawn) {
$scope.tree.notifyResized();
return;
}
$scope.drawn = true;
$timeout(function() {
$scope.tree.draw('file-tree-container');
}, 10);
};
var determineDockerfilePath = function() {
var dockerfilePath = 'Dockerfile';
var dockerfileFolder = ($scope.repobuild['subdirectory'] || '');
if (dockerfileFolder[0] == '/') {
dockerfileFolder = dockerfileFolder.substr(1);
}
if (dockerfileFolder && dockerfileFolder[dockerfileFolder.length - 1] != '/') {
dockerfileFolder += '/';
}
dockerfilePath = dockerfileFolder + 'Dockerfile';
return dockerfilePath;
};
var processBuildPack = function(uint8array) {
var archiveread = function(files) {
var getpath = function(file) {
return file.path;
};
var findFile = function(path) {
for (var i = 0; i < files.length; ++i) {
var file = files[i];
if (file.path == path) {
return file;
}
}
return null;
};
$scope.tree = new FileTree($.map(files, getpath));
$($scope.tree).bind('fileClicked', function(e) {
var file = findFile(e.path);
if (file && file.canRead) {
saveAs(file.toBlob(), file.name);
}
});
var dockerfilePath = determineDockerfilePath();
var dockerfile = findFile(dockerfilePath);
if (dockerfile && dockerfile.canRead) {
DataFileService.blobToString(dockerfile.toBlob(), function(result) {
$scope.$apply(function() {
$scope.dockerFilePath = dockerfilePath || 'Dockerfile';
$scope.dockerFileContents = result;
});
});
}
$scope.loaded = true;
};
var notarchive = function() {
DataFileService.arrayToString(uint8array, function(r) {
$scope.dockerFilePath = 'Dockerfile';
$scope.dockerFileContents = r;
$scope.loaded = true;
});
};
setTimeout(function() {
$scope.$apply(function() {
DataFileService.readDataArrayAsPossibleArchive(uint8array, archiveread, notarchive);
});
}, 0);
};
var downloadBuildPack = function(url) {
$scope.downloadProgress = 0;
$scope.downloading = true;
startDownload(url);
};
var startDownload = function(url) {
var onprogress = function(p) {
$scope.downloadProgress = p * 100;
};
var onerror = function() {
$scope.downloading = false;
$scope.downloadError = true;
};
var onloaded = function(uint8array) {
$scope.downloading = false;
processBuildPack(uint8array);
};
DataFileService.downloadDataFileAsArrayBuffer($scope, url, onprogress, onerror, onloaded);
};
var getBuildInfo = function() {
$scope.repository_build = ApiService.getRepoBuildStatus(null, params, true).then(function(resp) {
if (!resp['is_writer']) {
$rootScope.title = 'Unknown build';
$scope.accessDenied = true;
return;
}
$rootScope.title = 'Repository Build Pack - ' + resp['display_name'];
$scope.repobuild = resp;
$scope.repo = {
'namespace': namespace,
'name': name
};
downloadBuildPack(resp['archive_url']);
return resp;
});
};
getBuildInfo();
}
})();

View file

@ -1,20 +0,0 @@
(function() {
/**
* Experiment enable page: New layout
*/
angular.module('quayPages').config(['pages', function(pages) {
pages.create('exp-new-layout', 'exp-new-layout.html', ExpCtrl, {
'newLayout': true
});
}]);
function ExpCtrl($scope, CookieService) {
$scope.isEnabled = CookieService.get('quay.exp-new-layout') == 'true';
$scope.setEnabled = function(value) {
$scope.isEnabled = value;
CookieService.putPermanent('quay.exp-new-layout', value.toString());
document.location.reload();
};
}
}());

View file

@ -7,10 +7,7 @@
'newLayout': true,
'title': '{{ image.id }}',
'description': 'Image {{ image.id }}'
}, ['layout'])
pages.create('image-view', 'old-image-view.html', OldImageViewCtrl, {
}, ['old-layout']);
})
}]);
function ImageViewCtrl($scope, $routeParams, $rootScope, $timeout, ApiService, ImageMetadataService) {

View file

@ -18,7 +18,7 @@
});
UserService.updateUserIn($scope, function(user) {
if (!user.anonymous && Config.isNewLayout()) {
if (!user.anonymous) {
$location.path('/repository');
return;
}

View file

@ -7,12 +7,7 @@
'newLayout': true,
'title': 'Manage Application {{ application.name }}',
'description': 'Manage an OAuth application'
}, ['layout']);
pages.create('manage-application', 'old-manage-application.html', ManageApplicationCtrl, {
'title': 'Manage Application {{ application.name }}',
'description': 'Manage an OAuth application'
}, ['old-layout']);
});
}]);
function ManageApplicationCtrl($scope, $routeParams, $rootScope, $location, $timeout, OAuthService, ApiService, UserService, Config) {

View file

@ -7,12 +7,7 @@
'newLayout': true,
'title': 'New Organization',
'description': 'Create a new organization to manage teams and permissions'
}, ['layout']);
pages.create('new-organization', 'old-new-organization.html', NewOrgCtrl, {
'title': 'New Organization',
'description': 'Create a new organization to manage teams and permissions'
}, ['old-layout']);
});
}]);
function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, PlanService, ApiService, CookieService, Features) {

View file

@ -7,12 +7,7 @@
'newLayout': true,
'title': 'New Repository',
'description': 'Create a new Docker repository'
}, ['layout'])
pages.create('new-repo', 'old-new-repo.html', NewRepoCtrl, {
'title': 'New Repository',
'description': 'Create a new Docker repository'
}, ['old-layout']);
})
}]);
function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService, PlanService, TriggerService, Features) {

View file

@ -1,110 +0,0 @@
(function() {
/**
* DEPRECATED: Organization admin/settings page.
*/
angular.module('quayPages').config(['pages', function(pages) {
pages.create('org-admin', 'org-admin.html', OrgAdminCtrl);
}]);
function OrgAdminCtrl($rootScope, $scope, $timeout, Restangular, $routeParams, UserService, PlanService, ApiService, Features, UIService) {
var orgname = $routeParams.orgname;
// Load the list of plans.
if (Features.BILLING) {
PlanService.getPlans(function(plans) {
$scope.plans = plans;
$scope.plan_map = {};
for (var i = 0; i < plans.length; ++i) {
$scope.plan_map[plans[i].stripeId] = plans[i];
}
});
}
$scope.orgname = orgname;
$scope.membersLoading = true;
$scope.membersFound = null;
$scope.invoiceLoading = true;
$scope.logsShown = 0;
$scope.invoicesShown = 0;
$scope.applicationsShown = 0;
$scope.changingOrganization = false;
$scope.loadLogs = function() {
$scope.logsShown++;
};
$scope.loadApplications = function() {
$scope.applicationsShown++;
};
$scope.loadInvoices = function() {
$scope.invoicesShown++;
};
$scope.planChanged = function(plan) {
$scope.hasPaidPlan = plan && plan.price > 0;
};
$scope.$watch('organizationEmail', function(e) {
UIService.hidePopover('#changeEmailForm');
});
$scope.changeEmail = function() {
UIService.hidePopover('#changeEmailForm');
$scope.changingOrganization = true;
var params = {
'orgname': orgname
};
var data = {
'email': $scope.organizationEmail
};
ApiService.changeOrganizationDetails(data, params).then(function(org) {
$scope.changingOrganization = false;
$scope.changeEmailForm.$setPristine();
$scope.organization = org;
}, function(result) {
$scope.changingOrganization = false;
UIService.showFormError('#changeEmailForm', result);
});
};
$scope.loadMembers = function() {
if ($scope.membersFound) { return; }
$scope.membersLoading = true;
var params = {
'orgname': orgname
};
ApiService.getOrganizationMembers(null, params).then(function(resp) {
var membersArray = [];
for (var key in resp.members) {
if (resp.members.hasOwnProperty(key)) {
membersArray.push(resp.members[key]);
}
}
$scope.membersFound = membersArray;
$scope.membersLoading = false;
});
};
var loadOrganization = function() {
$scope.orgResource = ApiService.getOrganizationAsResource({'orgname': orgname}).get(function(org) {
if (org && org.is_admin) {
$scope.organization = org;
$scope.organizationEmail = org.email;
$rootScope.title = orgname + ' (Admin)';
$rootScope.description = 'Administration page for organization ' + orgname;
}
});
};
// Load the organization.
loadOrganization();
}
})();

View file

@ -1,49 +0,0 @@
(function() {
/**
* Page for displaying the logs of a member in an organization.
*/
angular.module('quayPages').config(['pages', function(pages) {
pages.create('org-member-logs', 'org-member-logs.html', OrgMemberLogsCtrl);
}]);
function OrgMemberLogsCtrl($scope, $routeParams, $rootScope, $timeout, Restangular, ApiService) {
var orgname = $routeParams.orgname;
var membername = $routeParams.membername;
$scope.orgname = orgname;
$scope.memberInfo = null;
$scope.ready = false;
var loadOrganization = function() {
$scope.orgResource = ApiService.getOrganizationAsResource({'orgname': orgname}).get(function(org) {
$scope.organization = org;
return org;
});
};
var loadMemberInfo = function() {
var params = {
'orgname': orgname,
'membername': membername
};
$scope.memberResource = ApiService.getOrganizationMemberAsResource(params).get(function(resp) {
$scope.memberInfo = resp.member;
$rootScope.title = 'Logs for ' + $scope.memberInfo.name + ' (' + $scope.orgname + ')';
$rootScope.description = 'Shows all the actions of ' + $scope.memberInfo.username +
' under organization ' + $scope.orgname;
$timeout(function() {
$scope.ready = true;
});
return resp.member;
});
};
// Load the org info and the member info.
loadOrganization();
loadMemberInfo();
}
})();

View file

@ -7,10 +7,7 @@
'newLayout': true,
'title': 'Organization {{ organization.name }}',
'description': 'Organization {{ organization.name }}'
}, ['layout'])
pages.create('org-view', 'old-org-view.html', OldOrgViewCtrl, {
}, ['old-layout']);
})
}]);
function OrgViewCtrl($scope, $routeParams, $timeout, ApiService, UIService, AvatarService) {

View file

@ -1,399 +0,0 @@
(function() {
/**
* DEPRECATED: Repository admin/settings page.
*/
angular.module('quayPages').config(['pages', function(pages) {
pages.create('repo-admin', 'repo-admin.html', RepoAdminCtrl);
}]);
function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, TriggerService, $routeParams,
$rootScope, $location, UserService, Config, Features, ExternalNotificationData, UtilService) {
var namespace = $routeParams.namespace;
var name = $routeParams.name;
$scope.Features = Features;
$scope.TriggerService = TriggerService;
$scope.KeyService = KeyService;
$scope.permissions = {'team': [], 'user': [], 'loading': 2};
$scope.logsShown = 0;
$scope.deleting = false;
$scope.permissionCache = {};
$scope.showTriggerSetupCounter = 0;
$scope.getBadgeFormat = function(format, repo) {
if (!repo) { return; }
var imageUrl = Config.getUrl('/repository/' + namespace + '/' + name + '/status');
if (!$scope.repo.is_public) {
imageUrl += '?token=' + $scope.repo.status_token;
}
var linkUrl = Config.getUrl('/repository/' + namespace + '/' + name);
switch (format) {
case 'svg':
return imageUrl;
case 'md':
return '[![Docker Repository on ' + Config.REGISTRY_TITLE_SHORT + '](' + imageUrl +
' "Docker Repository on ' + Config.REGISTRY_TITLE_SHORT + '")](' + linkUrl + ')';
case 'asciidoc':
return 'image:' + imageUrl + '["Docker Repository on ' + Config.REGISTRY_TITLE_SHORT + '", link="' + linkUrl + '"]';
}
return '';
};
$scope.buildEntityForPermission = function(name, permission, kind) {
var key = name + ':' + kind;
if ($scope.permissionCache[key]) {
return $scope.permissionCache[key];
}
return $scope.permissionCache[key] = {
'kind': kind,
'name': name,
'is_robot': permission.is_robot,
'is_org_member': permission.is_org_member
};
};
$scope.loadLogs = function() {
$scope.logsShown++;
};
$scope.grantRole = function() {
$('#confirmaddoutsideModal').modal('hide');
var entity = $scope.currentAddEntity;
$scope.addRole(entity.name, 'read', entity.kind, entity.is_org_member)
$scope.currentAddEntity = null;
};
$scope.addNewPermission = function(entity) {
// Don't allow duplicates.
if (!entity || !entity.kind || $scope.permissions[entity.kind][entity.name]) { return; }
if (entity.is_org_member === false) {
$scope.currentAddEntity = entity;
$('#confirmaddoutsideModal').modal('show');
return;
}
$scope.addRole(entity.name, 'read', entity.kind);
};
$scope.deleteRole = function(entityName, kind) {
var errorHandler = ApiService.errorDisplay('Cannot change permission', function(resp) {
if (resp.status == 409) {
return 'Cannot change permission as you do not have the authority';
}
});
var permissionDelete = Restangular.one(UtilService.getRestUrl('repository', namespace, name, 'permissions', kind, entityName));
permissionDelete.customDELETE().then(function() {
delete $scope.permissions[kind][entityName];
}, errorHandler);
};
$scope.addRole = function(entityName, role, kind) {
var permission = {
'role': role,
};
var permissionPost = Restangular.one(UtilService.getRestUrl('repository', namespace, name, 'permissions', kind, entityName));
permissionPost.customPUT(permission).then(function(result) {
$scope.permissions[kind][entityName] = result;
}, ApiService.errorDisplay('Cannot change permission'));
};
$scope.setRole = function(role, entityName, kind) {
var permission = $scope.permissions[kind][entityName];
var currentRole = permission.role;
permission.role = role;
var permissionPut = Restangular.one(UtilService.getRestUrl('repository', namespace, name, 'permissions', kind, entityName));
permissionPut.customPUT(permission).then(function() {}, function(resp) {
$scope.permissions[kind][entityName] = {'role': currentRole};
$scope.changePermError = null;
if (resp.status == 409 || resp.data) {
$scope.changePermError = resp.data || '';
$('#channgechangepermModal').modal({});
} else {
$('#cannotchangeModal').modal({});
}
});
};
$scope.newTokenName = null;
$scope.createToken = function() {
var data = {
'friendlyName': $scope.newTokenName
};
var params = {'repository': namespace + '/' + name};
ApiService.createToken(data, params).then(function(newToken) {
$scope.newTokenName = null;
$scope.createTokenForm.$setPristine();
$scope.tokens[newToken.code] = newToken;
});
};
$scope.deleteToken = function(tokenCode) {
var params = {
'repository': namespace + '/' + name,
'code': tokenCode
};
ApiService.deleteToken(null, params).then(function() {
delete $scope.tokens[tokenCode];
});
};
$scope.changeTokenAccess = function(tokenCode, newAccess) {
var role = {
'role': newAccess
};
var params = {
'repository': namespace + '/' + name,
'code': tokenCode
};
ApiService.changeToken(role, params).then(function(updated) {
$scope.tokens[updated.code] = updated;
});
};
$scope.shownTokenCounter = 0;
$scope.showToken = function(tokenCode) {
$scope.shownToken = $scope.tokens[tokenCode];
$scope.shownTokenCounter++;
};
$scope.askChangeAccess = function(newAccess) {
$('#make' + newAccess + 'Modal').modal({});
};
$scope.changeAccess = function(newAccess) {
$('#make' + newAccess + 'Modal').modal('hide');
var visibility = {
'visibility': newAccess
};
var params = {
'repository': namespace + '/' + name
};
ApiService.changeRepoVisibility(visibility, params).then(function() {
$scope.repo.is_public = newAccess == 'public';
}, function() {
$('#cannotchangeModal').modal({});
});
};
$scope.askDelete = function() {
$('#confirmdeleteModal').modal({});
};
$scope.deleteRepo = function() {
$('#confirmdeleteModal').modal('hide');
var params = {
'repository': namespace + '/' + name
};
$scope.deleting = true;
ApiService.deleteRepository(null, params).then(function() {
$scope.repo = null;
setTimeout(function() {
document.location = '/repository/';
}, 1000);
}, function() {
$scope.deleting = true;
$('#cannotchangeModal').modal({});
});
};
$scope.showNewNotificationCounter = 0;
$scope.showNewNotificationDialog = function() {
$scope.showNewNotificationCounter++;
};
$scope.handleNotificationCreated = function(notification) {
$scope.notifications.push(notification);
};
$scope.handleNotificationDeleted = function(notification) {
var index = $.inArray(notification, $scope.notifications);
if (index < 0) { return; }
$scope.notifications.splice(index, 1);
};
$scope.loadNotifications = function() {
var params = {
'repository': namespace + '/' + name
};
$scope.notificationsResource = ApiService.listRepoNotificationsAsResource(params).get(
function(resp) {
$scope.notifications = resp.notifications;
return $scope.notifications;
});
};
$scope.showBuild = function(buildInfo) {
$location.path('/repository/' + namespace + '/' + name + '/build');
$location.search('current', buildInfo.id);
};
$scope.loadTriggerBuildHistory = function(trigger) {
trigger.$loadingHistory = true;
var params = {
'repository': namespace + '/' + name,
'trigger_uuid': trigger.id,
'limit': 3
};
ApiService.listTriggerRecentBuilds(null, params).then(function(resp) {
trigger.$builds = resp['builds'];
trigger.$loadingHistory = false;
});
};
$scope.loadTriggers = function() {
var params = {
'repository': namespace + '/' + name
};
$scope.triggersResource = ApiService.listBuildTriggersAsResource(params).get(function(resp) {
$scope.triggers = resp.triggers;
// Check to see if we need to setup any trigger.
var newTriggerId = $routeParams.new_trigger;
if (newTriggerId) {
for (var i = 0; i < $scope.triggers.length; ++i) {
var trigger = $scope.triggers[i];
if (trigger['id'] == newTriggerId && !trigger['is_active']) {
$scope.setupTrigger(trigger);
break;
}
}
}
return $scope.triggers;
});
};
$scope.setupTrigger = function(trigger) {
$scope.currentSetupTrigger = trigger;
$scope.showTriggerSetupCounter++;
};
$scope.cancelSetupTrigger = function(trigger) {
if ($scope.currentSetupTrigger != trigger) { return; }
$scope.currentSetupTrigger = null;
$scope.deleteTrigger(trigger);
};
$scope.showManualBuildDialog = 0;
$scope.startTrigger = function(trigger, opt_custom) {
var parameters = TriggerService.getRunParameters(trigger.service);
if (parameters.length && !opt_custom) {
$scope.currentStartTrigger = trigger;
$scope.showManualBuildDialog++;
return;
}
var params = {
'repository': namespace + '/' + name,
'trigger_uuid': trigger.id
};
ApiService.manuallyStartBuildTrigger(opt_custom || {}, params).then(function(resp) {
var url = '/repository/' + namespace + '/' + name + '/build?current=' + resp['id'];
document.location = url;
}, ApiService.errorDisplay('Could not start build'));
};
$scope.deleteTrigger = function(trigger) {
if (!trigger) { return; }
var params = {
'repository': namespace + '/' + name,
'trigger_uuid': trigger.id
};
ApiService.deleteBuildTrigger(null, params).then(function(resp) {
$scope.triggers.splice($scope.triggers.indexOf(trigger), 1);
});
};
var fetchTokens = function() {
var params = {
'repository': namespace + '/' + name
};
ApiService.listRepoTokens(null, params).then(function(resp) {
$scope.tokens = resp.tokens;
}, function() {
$scope.tokens = null;
});
};
var fetchPermissions = function(kind) {
var permissionsFetch = Restangular.one('repository/' + namespace + '/' + name + '/permissions/' + kind + '/');
permissionsFetch.get().then(function(resp) {
$scope.permissions[kind] = resp.permissions;
$scope.permissions['loading']--;
}, function() {
$scope.permissions[kind] = null;
});
};
var fetchRepository = function() {
var params = {
'repository': namespace + '/' + name
};
$scope.repository = ApiService.getRepoAsResource(params).get(function(repo) {
if (!repo.can_admin) {
$rootScope.title = 'Forbidden';
$scope.accessDenied = true;
return;
}
$scope.repo = repo;
$rootScope.title = 'Settings - ' + namespace + '/' + name;
$rootScope.description = 'Administrator settings for ' + namespace + '/' + name +
': Permissions, notifications and other settings';
// Fetch all the permissions and token info for the repository.
fetchPermissions('user');
fetchPermissions('team');
fetchTokens();
$('.info-icon').popover({
'trigger': 'hover',
'html': true
});
return $scope.repo;
});
};
// Fetch the repository.
fetchRepository();
}
})();

View file

@ -1,294 +0,0 @@
(function() {
/**
* Repository Build view page. Displays the status of a repository build.
*/
angular.module('quayPages').config(['pages', function(pages) {
pages.create('repo-build', 'repo-build.html', RepoBuildCtrl);
}]);
function RepoBuildCtrl($scope, Restangular, ApiService, $routeParams, $rootScope, $location, $interval, $sanitize,
ansi2html, AngularViewArray, AngularPollChannel) {
var namespace = $routeParams.namespace;
var name = $routeParams.name;
// Watch for changes to the current parameter.
$scope.$on('$routeUpdate', function(){
if ($location.search().current) {
$scope.setCurrentBuild($location.search().current, false);
}
});
$scope.builds = null;
$scope.pollChannel = null;
$scope.buildDialogShowCounter = 0;
$scope.showNewBuildDialog = function() {
$scope.buildDialogShowCounter++;
};
$scope.handleBuildStarted = function(newBuild) {
if (!$scope.builds) { return; }
$scope.builds.unshift(newBuild);
$scope.setCurrentBuild(newBuild['id'], true);
};
$scope.adjustLogHeight = function() {
var triggerOffset = 0;
if ($scope.currentBuild && $scope.currentBuild.trigger) {
triggerOffset = 85;
}
$('.build-logs').height($(window).height() - 415 - triggerOffset);
};
$scope.askRestartBuild = function(build) {
$('#confirmRestartBuildModal').modal({});
};
$scope.askCancelBuild = function(build) {
bootbox.confirm('Are you sure you want to cancel this build?', function(r) {
if (r) {
var params = {
'repository': namespace + '/' + name,
'build_uuid': build.id
};
ApiService.cancelRepoBuild(null, params).then(function() {
if (!$scope.builds) { return; }
$scope.builds.splice($.inArray(build, $scope.builds), 1);
if ($scope.builds.length) {
$scope.currentBuild = $scope.builds[0];
} else {
$scope.currentBuild = null;
}
}, ApiService.errorDisplay('Cannot cancel build'));
}
});
};
$scope.restartBuild = function(build) {
$('#confirmRestartBuildModal').modal('hide');
var subdirectory = build['subdirectory'] || '';
var data = {
'file_id': build['resource_key'],
'subdirectory': subdirectory,
'docker_tags': build['tags']
};
if (build['pull_robot']) {
data['pull_robot'] = build['pull_robot']['name'];
}
var params = {
'repository': namespace + '/' + name
};
ApiService.requestRepoBuild(data, params).then(function(newBuild) {
if (!$scope.builds) { return; }
$scope.builds.unshift(newBuild);
$scope.setCurrentBuild(newBuild['id'], true);
});
};
$scope.hasLogs = function(container) {
return container.logs.hasEntries;
};
$scope.setCurrentBuild = function(buildId, opt_updateURL) {
if (!$scope.builds) { return; }
// Find the build.
for (var i = 0; i < $scope.builds.length; ++i) {
if ($scope.builds[i].id == buildId) {
$scope.setCurrentBuildInternal(i, $scope.builds[i], opt_updateURL);
return;
}
}
};
$scope.processANSI = function(message, container) {
var filter = container.logs._filter = (container.logs._filter || ansi2html.create());
// Note: order is important here.
var setup = filter.getSetupHtml();
var stream = filter.addInputToStream(message);
var teardown = filter.getTeardownHtml();
return setup + stream + teardown;
};
$scope.setCurrentBuildInternal = function(index, build, opt_updateURL) {
if (build == $scope.currentBuild) { return; }
$scope.logEntries = null;
$scope.logStartIndex = null;
$scope.currentParentEntry = null;
$scope.currentBuild = build;
if (opt_updateURL) {
if (build) {
$location.search('current', build.id);
} else {
$location.search('current', null);
}
}
// Timeout needed to ensure the log element has been created
// before its height is adjusted.
setTimeout(function() {
$scope.adjustLogHeight();
}, 1);
// Stop any existing polling.
if ($scope.pollChannel) {
$scope.pollChannel.stop();
}
// Create a new channel for polling the build status and logs.
var conductStatusAndLogRequest = function(callback) {
getBuildStatusAndLogs(build, callback);
};
$scope.pollChannel = AngularPollChannel.create($scope, conductStatusAndLogRequest, 5 * 1000 /* 5s */);
$scope.pollChannel.start();
};
var processLogs = function(logs, startIndex, endIndex) {
if (!$scope.logEntries) { $scope.logEntries = []; }
// If the start index given is less than that requested, then we've received a larger
// pool of logs, and we need to only consider the new ones.
if (startIndex < $scope.logStartIndex) {
logs = logs.slice($scope.logStartIndex - startIndex);
}
for (var i = 0; i < logs.length; ++i) {
var entry = logs[i];
var type = entry['type'] || 'entry';
if (type == 'command' || type == 'phase' || type == 'error') {
entry['logs'] = AngularViewArray.create();
entry['index'] = $scope.logStartIndex + i;
$scope.logEntries.push(entry);
$scope.currentParentEntry = entry;
} else if ($scope.currentParentEntry) {
$scope.currentParentEntry['logs'].push(entry);
}
}
return endIndex;
};
var handleLogsData = function(logsData, callback) {
// Process the logs we've received.
$scope.logStartIndex = processLogs(logsData['logs'], logsData['start'], logsData['total']);
// If the build status is an error, open the last two log entries.
var currentBuild = $scope.currentBuild;
if (currentBuild['phase'] == 'error' && $scope.logEntries.length > 1) {
var openLogEntries = function(entry) {
if (entry.logs) {
entry.logs.setVisible(true);
}
};
openLogEntries($scope.logEntries[$scope.logEntries.length - 2]);
openLogEntries($scope.logEntries[$scope.logEntries.length - 1]);
}
// If the build phase is an error or a complete, then we mark the channel
// as closed.
callback(currentBuild['phase'] != 'error' && currentBuild['phase'] != 'complete');
};
var getBuildStatusAndLogs = function(build, callback) {
var params = {
'repository': namespace + '/' + name,
'build_uuid': build.id
};
ApiService.getRepoBuildStatus(null, params, true).then(function(resp) {
if (build != $scope.currentBuild) { callback(false); return; }
// Note: We use extend here rather than replacing as Angular is depending on the
// root build object to remain the same object.
var matchingBuilds = $.grep($scope.builds, function(elem) {
return elem['id'] == resp['id']
});
var currentBuild = matchingBuilds.length > 0 ? matchingBuilds[0] : null;
if (currentBuild) {
currentBuild = $.extend(true, currentBuild, resp);
} else {
currentBuild = resp;
$scope.builds.push(currentBuild);
}
// Load the updated logs for the build.
var options = {
'start': $scope.logStartIndex
};
ApiService.getRepoBuildLogsAsResource(params, true).withOptions(options).get(function(resp) {
if (build != $scope.currentBuild) { callback(false); return; }
// If we get a logs url back, then we need to make another XHR request to retrieve the
// data.
if (resp['logs_url']) {
$.ajax({
url: resp['logs_url'],
}).done(function(r) {
handleLogsData(r, callback);
});
return;
}
handleLogsData(resp, callback);
}, function() {
callback(false);
});
}, function() {
callback(false);
});
};
var fetchRepository = function() {
var params = {'repository': namespace + '/' + name};
$rootScope.title = 'Loading Repository...';
$scope.repository = ApiService.getRepoAsResource(params).get(function(repo) {
if (!repo.can_write) {
$rootScope.title = 'Unknown builds';
$scope.accessDenied = true;
return;
}
$rootScope.title = 'Repository Builds';
$scope.repo = repo;
getBuildInfo();
});
};
var getBuildInfo = function(repo) {
var params = {
'repository': namespace + '/' + name
};
ApiService.getRepoBuilds(null, params).then(function(resp) {
$scope.builds = resp.builds;
if ($location.search().current) {
$scope.setCurrentBuild($location.search().current, false);
} else if ($scope.builds.length > 0) {
$scope.setCurrentBuild($scope.builds[0].id, true);
}
});
};
fetchRepository();
}
})();

View file

@ -7,12 +7,7 @@
'newLayout': true,
'title': 'Repositories',
'description': 'View and manage Docker repositories'
}, ['layout'])
pages.create('repo-list', 'old-repo-list.html', OldRepoListCtrl, {
'title': 'Repositories',
'description': 'View and manage Docker repositories'
}, ['old-layout']);
})
}]);

View file

@ -7,10 +7,7 @@
'newLayout': true,
'title': '{{ namespace }}/{{ name }}',
'description': 'Repository {{ namespace }}/{{ name }}'
}, ['layout'])
pages.create('repo-view', 'old-repo-view.html', OldRepoViewCtrl, {
}, ['old-layout']);
})
}]);
function RepoViewCtrl($scope, $routeParams, $location, $timeout, ApiService, UserService, AngularPollChannel) {

View file

@ -8,10 +8,7 @@
{
'newLayout': true,
'title': 'Enterprise Registry Setup'
},
// Note: This page has already been converted, but also needs to be available in the old layout
['layout', 'old-layout'])
})
}]);
function SetupCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, CoreDialog) {

View file

@ -7,10 +7,7 @@
{
'newLayout': true,
'title': 'Enterprise Registry Management'
},
// Note: This page has already been converted, but also needs to be available in the old layout
['layout', 'old-layout'])
})
}]);
function SuperuserCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, AngularPollChannel, CoreDialog) {

View file

@ -7,10 +7,7 @@
'newLayout': true,
'title': 'Team {{ teamname }}',
'description': 'Team {{ teamname }}'
}, ['layout'])
pages.create('team-view', 'old-team-view.html', TeamViewCtrl, {
}, ['old-layout']);
})
}]);
function TeamViewCtrl($rootScope, $scope, $timeout, Features, Restangular, ApiService, $routeParams) {

View file

@ -7,10 +7,7 @@
'newLayout': true,
'title': 'Tutorial',
'description': 'Basic tutorial on using Quay.io'
}, ['layout'])
pages.create('tutorial', 'old-tutorial.html', TutorialCtrl, {
}, ['old-layout']);
})
}]);
function TutorialCtrl($scope, AngularTour, AngularTourSignals, UserService, Config, Features) {

View file

@ -1,222 +0,0 @@
(function() {
/**
* DEPRECATED: User admin/settings page.
*/
angular.module('quayPages').config(['pages', function(pages) {
pages.create('user-admin', 'user-admin.html', UserAdminCtrl, {
'title': 'User Settings'
});
}]);
function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, UserService, CookieService, KeyService,
$routeParams, $http, UIService, Features, Config) {
$scope.Features = Features;
if ($routeParams['migrate']) {
$('#migrateTab').tab('show')
}
UserService.updateUserIn($scope, function(user) {
$scope.cuser = jQuery.extend({}, user);
if ($scope.cuser.logins) {
for (var i = 0; i < $scope.cuser.logins.length; i++) {
var login = $scope.cuser.logins[i];
login.metadata = login.metadata || {};
if (login.service == 'github') {
$scope.hasGithubLogin = true;
$scope.githubLogin = login.metadata['service_username'];
$scope.githubEndpoint = KeyService['githubEndpoint'];
}
if (login.service == 'google') {
$scope.hasGoogleLogin = true;
$scope.googleLogin = login.metadata['service_username'];
}
}
}
});
$scope.readyForPlan = function() {
// Show the subscribe dialog if a plan was requested.
return $routeParams['plan'];
};
$scope.loading = true;
$scope.updatingUser = false;
$scope.changePasswordSuccess = false;
$scope.changeEmailSent = false;
$scope.convertStep = 0;
$scope.org = {};
$scope.githubRedirectUri = KeyService.githubRedirectUri;
$scope.authorizedApps = null;
$scope.logsShown = 0;
$scope.invoicesShown = 0;
$scope.USER_PATTERN = USER_PATTERN;
$scope.loadAuthedApps = function() {
if ($scope.authorizedApps) { return; }
ApiService.listUserAuthorizations().then(function(resp) {
$scope.authorizedApps = resp['authorizations'];
});
};
$scope.deleteAccess = function(accessTokenInfo) {
var params = {
'access_token_uuid': accessTokenInfo['uuid']
};
ApiService.deleteUserAuthorization(null, params).then(function(resp) {
$scope.authorizedApps.splice($scope.authorizedApps.indexOf(accessTokenInfo), 1);
}, ApiService.errorDisplay('Could not revoke authorization'));
};
$scope.loadLogs = function() {
if (!$scope.hasPaidBusinessPlan) { return; }
$scope.logsShown++;
};
$scope.loadInvoices = function() {
$scope.invoicesShown++;
};
$scope.planChanged = function(plan) {
$scope.hasPaidPlan = plan && plan.price > 0;
$scope.hasPaidBusinessPlan = PlanService.isOrgCompatible(plan) && plan.price > 0;
};
$scope.showConvertForm = function() {
if (Features.BILLING) {
PlanService.getMatchingBusinessPlan(function(plan) {
$scope.org.plan = plan;
});
PlanService.getPlans(function(plans) {
$scope.orgPlans = plans;
});
}
$scope.convertStep = 1;
};
$scope.convertToOrg = function() {
$('#reallyconvertModal').modal({});
};
$scope.reallyConvert = function() {
if (Config.AUTHENTICATION_TYPE != 'Database') { return; }
$scope.loading = true;
var data = {
'adminUser': $scope.org.adminUser,
'adminPassword': $scope.org.adminPassword,
'plan': $scope.org.plan ? $scope.org.plan.stripeId : ''
};
ApiService.convertUserToOrganization(data).then(function(resp) {
CookieService.putPermanent('quay.namespace', $scope.cuser.username);
UserService.load();
$location.path('/');
}, function(resp) {
$scope.loading = false;
if (resp.data.reason == 'invaliduser') {
$('#invalidadminModal').modal({});
} else {
$('#cannotconvertModal').modal({});
}
});
};
$scope.changeUsername = function() {
UserService.load();
$scope.updatingUser = true;
ApiService.changeUserDetails($scope.cuser).then(function() {
$scope.updatingUser = false;
// Reset the form.
delete $scope.cuser['username'];
$scope.changeUsernameForm.$setPristine();
}, function(result) {
$scope.updatingUser = false;
UIService.showFormError('#changeUsernameForm', result);
});
};
$scope.changeEmail = function() {
UIService.hidePopover('#changeEmailForm');
$scope.updatingUser = true;
$scope.changeEmailSent = false;
ApiService.changeUserDetails($scope.cuser).then(function() {
$scope.updatingUser = false;
$scope.changeEmailSent = true;
$scope.sentEmail = $scope.cuser.email;
delete $scope.cuser['email'];
}, function(result) {
$scope.updatingUser = false;
UIService.showFormError('#changeEmailForm', result);
});
};
$scope.changePassword = function() {
UIService.hidePopover('#changePasswordForm');
$scope.updatingUser = true;
$scope.changePasswordSuccess = false;
ApiService.changeUserDetails($scope.cuser).then(function(resp) {
$scope.updatingUser = false;
$scope.changePasswordSuccess = true;
// Reset the form
delete $scope.cuser['password']
delete $scope.cuser['repeatPassword']
$scope.changePasswordForm.$setPristine();
// Reload the user.
UserService.load();
}, function(result) {
$scope.updatingUser = false;
UIService.showFormError('#changePasswordForm', result);
});
};
$scope.generateClientToken = function() {
var generateToken = function(password) {
var data = {
'password': password
};
ApiService.generateUserClientKey(data).then(function(resp) {
$scope.generatedClientToken = resp['key'];
$('#clientTokenModal').modal({});
}, ApiService.errorDisplay('Could not generate token'));
};
UIService.showPasswordDialog('Enter your password to generated an encrypted version:', generateToken);
};
$scope.detachExternalLogin = function(kind) {
var params = {
'servicename': kind
};
ApiService.detachExternalLogin(null, params).then(function() {
$scope.hasGithubLogin = false;
$scope.hasGoogleLogin = false;
UserService.load();
}, ApiService.errorDisplay('Count not detach service'));
};
}
})();

View file

@ -7,7 +7,7 @@
'newLayout': true,
'title': 'User {{ user.username }}',
'description': 'User {{ user.username }}'
}, ['layout'])
})
}]);
function UserViewCtrl($scope, $routeParams, $timeout, ApiService, UserService, UIService, AvatarService) {

View file

@ -14,9 +14,7 @@ angular.module('quay').factory('AvatarService', ['Config', '$sanitize', 'md5',
break;
case 'gravatar':
// TODO(jschorr): Remove once the new layout is in place everywhere.
var default_kind = Config.isNewLayout() ? '404' : 'identicon';
return '//www.gravatar.com/avatar/' + hash + '?d=' + default_kind + '&size=' + size;
return '//www.gravatar.com/avatar/' + hash + '?d=404&size=' + size;
break;
}
};

View file

@ -71,10 +71,5 @@ angular.module('quay').factory('Config', [function() {
return value;
};
config.isNewLayout = function() {
// TODO(jschorr): Remove in the cleanup CL.
return true;
};
return config;
}]);

View file

@ -56,20 +56,10 @@ function($rootScope, $interval, UserService, ApiService, StringBuilderService, P
'<br><br>Please upgrade your plan to avoid disruptions in service.',
'page': function(metadata) {
var organization = UserService.getOrganization(metadata['namespace']);
// TODO(jschorr): Remove once the new layout is in prod.
if (Config.isNewLayout()) {
if (organization) {
return '/organization/' + metadata['namespace'] + '?tab=billing';
} else {
return '/user/' + metadata['namespace'] + '?tab=billing';
}
}
if (organization) {
return '/organization/' + metadata['namespace'] + '/admin';
return '/organization/' + metadata['namespace'] + '?tab=billing';
} else {
return '/user';
return '/user/' + metadata['namespace'] + '?tab=billing';
}
}
},

View file

@ -1,53 +0,0 @@
<div class="resource-view" resource="repository_build" error-message="'No matching repository build found'"></div>
<div class="cor-container repo repo-build" ng-show="accessDenied">
You do not have permission to view this page
</div>
<div class="cor-container repo repo-build repo-build-pack" ng-show="repobuild">
<div class="header row">
<a href="{{ '/repository/' + repo.namespace + '/' + repo.name + '/build' }}" class="back"><i class="fa fa-chevron-left"></i></a>
<h3>
<span class="repo-circle no-background" repo="repo"></span>
<span class="repo-breadcrumb" repo="repo" subsection-icon="'fa-tasks'" subsection="repobuild.display_name"></span>
</h3>
</div>
<div class="row" ng-show="downloading">
Downloading build pack:
<div class="progress" class="active progress-striped">
<div class="progress-bar" role="progressbar" aria-valuenow="{{ downloadProgress }}" aria-valuemin="0" aria-valuemax="100" style="{{ 'width: ' + downloadProgress + '%' }}">
</div>
</div>
</div>
<div class="row" ng-show="downloadError">
Error: Could not download the build pack
</div>
<div class="row" ng-show="!downloading && !downloadError && !loaded">
Reading... <span class="quay-spinner"></span>
</div>
<div class="row" ng-show="loaded">
<ul class="nav nav-tabs">
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#dockerfile">Dockerfile</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#tree" ng-click="initializeTree()" ng-show="tree">All Files</a></li>
</ul>
<div class="tab-content">
<!-- Dockerfile view -->
<div class="tab-pane active" id="dockerfile">
<div class="dockerfile-path" ng-if="dockerFileContents">{{ dockerFilePath }}</div>
<div class="dockerfile-view" contents="dockerFileContents" ng-if="dockerFileContents"></div>
<div ng-if="!dockerFileContents" style="padding: 10px; margin-top: 20px;">
No Dockerfile found in the build pack
</div>
</div>
<!-- File tree -->
<div class="tab-pane" id="tree">
<div id="file-tree-container" class="tree-container" onresize="tree && drawn && tree.notifyResized()"></div>
</div>
</div>
</div>
</div>

View file

@ -1,10 +0,0 @@
<div class="page-content">
<div class="cor-title">
<span class="cor-title-link"></span>
<span class="cor-title-content">Experiment: New Layout</span>
</div>
<div class="co-main-content-panel">
<button class="btn btn-success" ng-if="!isEnabled" ng-click="setEnabled(true)">Enable Experiment</button>
<button class="btn btn-failure" ng-if="isEnabled" ng-click="setEnabled(false)">Disable Experiment</button>
</div>
</div>

View file

@ -1,82 +0,0 @@
<div class="resource-view" resource="image" error-message="'No image found'">
<div class="cor-container repo repo-image-view">
<div class="header">
<a href="{{ '/repository/' + repo.namespace + '/' + repo.name }}" class="back"><i class="fa fa-chevron-left"></i></a>
<h3>
<span class="repo-circle no-background" repo="repo"></span>
<span class="repo-breadcrumb" repo="repo" image="image.value"></span>
</h3>
</div>
<!-- Comment -->
<blockquote ng-show="image.value.comment">
<span class="markdown-view" content="image.value.comment"></span>
</blockquote>
<!-- Information -->
<dl class="dl-normal">
<dt>Full Image ID</dt>
<dd>
<div class="copy-box" value="image.value.id"></div>
</dd>
<dt>Created</dt>
<dd am-time-ago="parseDate(image.value.created)"></dd>
<dt>Compressed Image Size</dt>
<dd><span class="context-tooltip"
data-title="The amount of data sent between Docker and the registry when pushing/pulling"
bs-tooltip="tooltip.title" data-container="body">{{ image.value.size | bytes }}</span>
</dd>
<dt ng-show="image.value.command && image.value.command.length">Command</dt>
<dd ng-show="image.value.command && image.value.command.length">
<pre class="formatted-command">{{ getFormattedCommand(image.value) }}</pre>
</dd>
</dl>
<!-- Changes tabs -->
<div ng-show="combinedChanges.length > 0">
<b>File Changes:</b>
<br>
<br>
<ul class="nav nav-tabs">
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#filterable">Filterable View</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#tree" ng-click="initializeTree()">Tree View</a></li>
</ul>
</div>
<!-- Changes tab content -->
<div class="tab-content" ng-show="combinedChanges.length > 0">
<!-- Filterable view -->
<div class="tab-pane active" id="filterable">
<div class="changes-container full-changes-container">
<div class="change-side-controls">
<div class="result-count">
Showing {{(combinedChanges | filter:search | limitTo:50).length}} of {{(combinedChanges | filter:search).length}} results
</div>
<div class="filter-input">
<input id="change-filter" class="form-control" placeholder="Filter Changes" type="text" ng-model="search.$">
</div>
</div>
<div style="height: 28px;"></div>
<div class="changes-list well well-sm">
<div ng-show="(combinedChanges | filter:search | limitTo:1).length == 0">
No matching changes
</div>
<div class="change" ng-repeat="change in combinedChanges | filter:search | limitTo:50">
<i ng-class="{'added': 'fa fa-plus-square', 'removed': 'fa fa-minus-square', 'changed': 'fa fa-pencil-square'}[change.kind]"></i>
<span data-title="{{change.file}}">
<span style="color: #888;">
<span ng-repeat="folder in getFolders(change.file) track by $index"><a href="javascript:void(0)" ng-click="setFolderFilter(getFolder(change.file), $index)">{{folder}}</a>/</span></span><span>{{getFilename(change.file)}}</span>
</span>
</div>
</div>
</div>
</div>
<!-- Tree view -->
<div class="tab-pane" id="tree">
<div id="changes-tree-container" class="changes-container" onresize="tree && tree.notifyResized()"></div>
</div>
</div>
</div>
</div>

View file

@ -1,191 +0,0 @@
<div class="resource-view" resource="appResource" error-message="'Application not found'">
</div>
<div ng-show="application">
<div class="cor-container manage-application">
<!-- Header -->
<div class="row">
<div class="col-md-12">
<div class="auth-header">
<span class="avatar" size="48" email="application.avatar_email" name="application.name"></span>
<h2>{{ application.name || '(Untitled)' }}</h2>
<h4>
<span class="avatar" size="24" data="organization.avatar" style="vertical-align: middle; margin-right: 4px;"></span>
<span style="vertical-align: middle"><a href="/organization/{{ organization.name }}/admin">{{ organization.name }}</a></span>
</h4>
</div>
</div>
</div>
<div class="row" style="margin-top: 10px" ng-if="!application.redirect_uri">
<div class="alert alert-warning">
Warning: There is no OAuth Redirect setup for this application. Please enter it in the <strong>Settings</strong> tab.
</div>
</div>
<!-- Content -->
<div class="row" style="margin-top: 10px">
<!-- Side tabs -->
<div class="col-md-2">
<ul class="nav nav-pills nav-stacked">
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#settings">Settings</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#oauth">OAuth Information</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#gen-token">Generate Token</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#delete">Delete Application</a></li>
</ul>
</div>
<!-- Content -->
<div class="col-md-10">
<div class="tab-content">
<!-- Settings tab -->
<div id="settings" class="tab-pane active">
<form method="put" name="applicationForm" id="applicationForm" ng-submit="updateApplication()">
<div class="form-group nested">
<label for="fieldAppName">Application Name</label>
<input type="text" class="form-control" id="fieldAppName" placeholder="Application Name" required ng-model="application.name">
<div class="description">The name of the application that is displayed to users</div>
</div>
<div class="form-group nested">
<label for="fieldAppURI">Homepage URL</label>
<input type="url" class="form-control" id="fieldAppURI" placeholder="Homepage URL" required ng-model="application.application_uri">
<div class="description">The URL to which the application will link in the authorization view</div>
</div>
<div class="form-group nested">
<label for="fieldAppDescription">Description (optional)</label>
<input type="text" class="form-control" id="fieldAppURI" placeholder="Description" ng-model="application.description">
<div class="description">The user friendly description of the application</div>
</div>
<div class="form-group nested">
<label for="fieldAppAvatar">Avatar E-mail (optional)</label>
<input type="email" class="form-control" id="fieldAppAvatar" placeholder="Avatar E-mail" ng-model="application.avatar_email">
<div class="description">An e-mail address representing the <a href="http://docs.quay.io/glossary/avatar" target="_blank">Avatar</a> for the application. See above for the icon.</div>
</div>
<div class="form-group nested" style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #eee;">
<label for="fieldAppRedirect">Redirect/Callback URL Prefix</label>
<input type="url" class="form-control" id="fieldAppRedirect" placeholder="OAuth Redirect URL" ng-model="application.redirect_uri" required>
<div class="description">Allowed prefix for the application's OAuth redirection/callback URLs</div>
</div>
<div class="button-bar">
<button class="btn btn-success btn-large" type="submit" ng-disabled="applicationForm.$invalid || updating">
Update Application
</button>
<span class="quay-spinner" ng-show="updating"></span>
</div>
</form>
</div>
<!-- Delete tab -->
<div id="delete" class="tab-pane">
<div class="panel panel-default">
<div class="panel-body">
<div style="text-align: center">
<div class="alert alert-danger">Deleting an application <b>cannot be undone</b>. Any existing users of your application will <strong>break!</strong>. Here be dragons!</div>
<button class="btn btn-danger" ng-click="askDelete()">Delete Application</button>
</div>
</div>
</div>
</div>
<!-- Generate Token tab -->
<div id="gen-token" class="tab-pane">
<div style="margin-bottom: 20px">
Click the button below to generate a new <a href="http://tools.ietf.org/html/rfc6749#section-1.4" target="_new">OAuth 2 Access Token</a>.
</div>
<div style="margin-bottom: 20px">
<strong>Note:</strong> The generated token will act on behalf of user
<span class="avatar" data="user.avatar" size="16" style="margin-left: 6px; margin-right: 4px;"></span>
{{ user.username }}
</div>
<table>
<tr ng-repeat="(scopeName, scopeInfo) in OAuthService.SCOPES">
<td><label onclick="event.stopPropagation()"><input type="checkbox" value="scopeInfo[0]" ng-model="genScopes[scopeName]">{{ scopeInfo[3] }}</label></td>
</tr>
</table>
<a class="btn btn-success"
href="{{ Config.getUrl('/oauth/authorize?response_type=token&client_id=' + application.client_id + '&scope=' + getScopes(genScopes).join(',') + '&redirect_uri=display') }}"
ng-disabled="!getScopes(genScopes).length" target="_blank">
Generate Access Token
</a>
</div>
<!-- OAuth tab -->
<div id="oauth" class="tab-pane">
<table style="margin-top: 20px;">
<thead>
<th style="width: 150px"></th>
<th style="width: 250px"></th>
<th></th>
</thead>
<tr>
<td>Client ID:</td>
<td style="width: 250px">
<div class="copy-box" hovering-message="true" value="application.client_id"></div>
</td>
</tr>
<tr>
<td>Client Secret: <i class="fa fa-lock fa-lg" data-title="Keep this secret!" bs-tooltip style="margin-left: 10px"></i></td>
<td>
{{ application.client_secret }}
</td>
<td style="padding-left: 10px">
<button class="btn btn-primary" ng-click="askResetClientSecret()">Reset Client Secret</button>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal message dialog -->
<div class="modal fade" id="resetSecretModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Reset Client Secret?</h4>
</div>
<div class="modal-body">
<div class="alert alert-info">
Note that resetting the Client Secret for this application will <strong>not</strong> invalidate any user tokens.
</div>
<div>Are you sure you want to reset your Client Secret? Any existing users of this Secret <strong>will break!</strong></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="resetClientSecret()">Yes, I'm sure</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="deleteAppModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Delete Application?</h4>
</div>
<div class="modal-body">
Are you <b>absolutely, positively</b> sure you would like to delete this application? This <b>cannot be undone</b>.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" ng-click="deleteApplication()">Delete Application</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View file

@ -1,94 +0,0 @@
<div class="loading" ng-show="creating">
<div class="quay-spinner"></div>
</div>
<div class="cor-container create-org" ng-show="!creating">
<div class="row header-row">
<div class="col-md-12">
<h2>Create Organization</h2>
<div class="steps-container" ng-show="false">
<ul class="steps">
<li class="step" ng-class="!user || user.anonymous ? 'active' : ''">
<i class="fa fa-sign-in"></i>
<span class="title">Login with an account</span>
</li>
<li class="step" ng-class="!user.anonymous && !created ? 'active' : ''">
<i class="fa fa-gear"></i>
<span class="title">Setup your organization</span>
</li>
<li class="step" ng-class="!user.anonymous && created ? 'active' : ''">
<i class="fa fa-group"></i>
<span class="title">Create teams</span>
</li>
</ul>
</div>
</div>
</div>
<!-- Step 1 -->
<div class="row" ng-show="!user || user.anonymous">
<div class="col-md-10 col-md-offset-1 page-description">
In order to create a new organization, <b>you must first be signed in</b> as the
user that <b>will become an admin</b> for the organization.
</div>
<div class="col-sm-6 col-sm-offset-3">
<div class="step-container" >
<div class="user-setup" redirect-url="'/organizations/new'" sign-in-started="signinStarted()"
signed-in="signedIn()"></div>
</div>
</div>
</div>
<!-- Step 2 -->
<div class="row" ng-show="user && !user.anonymous && !created">
<div class="col-md-12">
<div class="step-container">
<h3>Setup the new organization</h3>
<form method="post" name="newOrgForm" id="newOrgForm" ng-submit="createNewOrg()">
<div class="form-group nested">
<label for="orgName">Organization Name</label>
<input id="orgName" name="orgName" type="text" class="form-control" placeholder="Organization Name"
ng-model="org.name" required autofocus data-trigger="manual" data-content="{{ createError }}"
data-placement="bottom" data-container="body" ng-pattern="/^[a-z0-9_]{4,30}$/">
<span class="description">This will also be the namespace for your repositories. Must be alphanumeric and all lowercase.</span>
</div>
<div class="form-group nested">
<label for="orgName">Organization Email</label>
<input id="orgEmail" name="orgEmail" type="email" class="form-control" placeholder="Organization Email"
ng-model="org.email" required>
<span class="description">This address must be different from your account's email.</span>
</div>
<!-- Plans Table -->
<div class="form-group nested plan-group" quay-require="['BILLING']">
<strong>Choose your organization's plan</strong>
<div class="plans-table" plans="plans" current-plan="holder.currentPlan"></div>
</div>
<div class="button-bar">
<button class="btn btn-large btn-success" type="submit"
ng-disabled="newOrgForm.$invalid || (Features.BILLING && !holder.currentPlan)"
analytics-on analytics-event="create_organization">
Create Organization
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Step 3 -->
<div class="row" ng-show="user && !user.anonymous && created">
<div class="col-md-12">
<div class="step-container">
<h3>Organization Created</h3>
<h4><a href="/organization/{{ org.name }}">Manage Teams Now</a></h4>
</div>
</div>
</div>
</div>

View file

@ -1,237 +0,0 @@
<div class="cor-container" ng-show="user.anonymous">
<div class="col-sm-6 col-sm-offset-3">
<div class="user-setup" redirect-url="'/new/'"></div>
</div>
</div>
<div class="cor-container" ng-show="!user.anonymous && building">
<div class="quay-spinner"></div>
</div>
<div class="cor-container" ng-show="!user.anonymous && creating">
<div class="quay-spinner"></div>
</div>
<div class="cor-container new-repo" ng-show="!user.anonymous && !creating && !building">
<form method="post" name="newRepoForm" id="newRepoForm" ng-submit="createNewRepo()">
<!-- Header -->
<div class="row">
<div class="col-md-12">
<div class="section">
<div class="new-header">
<span style="color: #444;">
<span class="namespace-selector" user="user" namespace="repo.namespace" require-create="true"></span>
<span style="color: #ccc">/</span>
<span class="name-container">
<input id="repoName" name="repoName" type="text" class="form-control" placeholder="Repository Name" ng-model="repo.name"
required autofocus data-trigger="manual" data-content="{{ createError }}" data-placement="right" ng-pattern="/^[.a-z0-9_-]+$/">
</span>
<span class="alert alert-warning" ng-show="!newRepoForm.repoName.$error.required && !newRepoForm.repoName.$valid" style="margin-left: 10px;">
Repository names must match [a-z0-9_-]+
</span>
</div>
</div>
<div class="section">
<div class="section-title">Repository Description</div>
<br>
<div class="description markdown-input" content="repo.description" can-write="true"
field-title="'repository description'"></div>
</div>
</div>
</div>
<!-- Private/public -->
<div class="row">
<div class="col-md-12">
<div class="section-title">Repository Visibility</div>
<div class="section">
<div class="repo-option">
<input type="radio" id="publicrepo" name="publicorprivate" ng-model="repo.is_public" value="1">
<i class="fa fa-unlock fa-large" data-title="Public Repository"></i>
<div class="option-description">
<label for="publicrepo"><strong>Public</strong></label>
<span class="description-text">Anyone can see and pull from this repository. You choose who can push.</span>
</div>
</div>
<div class="repo-option">
<input type="radio" id="privaterepo" name="publicorprivate" ng-model="repo.is_public" value="0">
<i class="fa fa-lock fa-large" data-title="Private Repository"></i>
<div class="option-description">
<label for="privaterepo"><strong>Private</strong></label>
<span class="description-text">You choose who can see, pull and push from/to this repository.</span>
</div>
</div>
<!-- Payment -->
<div class="required-plan" ng-show="repo.is_public == '0' && planRequired && planRequired.title">
<div class="alert alert-warning">
In order to make this repository private
<span ng-if="isUserNamespace">under your personal namespace</span>
<span ng-if="!isUserNamespace">under the organization <b>{{ repo.namespace }}</b></span>, you will need to upgrade your plan to
<b style="border-bottom: 1px dotted black;" data-html="true"
data-title="{{ '<b>' + planRequired.title + '</b><br>' + planRequired.privateRepos + ' private repositories' }}" bs-tooltip>
{{ planRequired.title }}
</b>.
This will cost $<span>{{ planRequired.price / 100 }}</span>/month.
</div>
<a class="btn btn-primary" ng-click="upgradePlan()" ng-show="!planChanging">Upgrade now</a>
<span ng-if="isUserNamespace && user.organizations.length == 1">or did you mean to create this repository
under <a href="javascript:void(0)" ng-click="changeNamespace(user.organizations[0].name)"><b>{{ user.organizations[0].name }}</b></a>?</span>
<div class="quay-spinner" ng-show="planChanging"></div>
</div>
<div class="quay-spinner" ng-show="repo.is_public == '0' && checkingPlan"></div>
<div class="required-plan" ng-show="repo.is_public == '0' && planRequired && !isUserNamespace && !planRequired.title">
<div class="alert alert-warning">
This organization has reached its private repository limit. Please contact your administrator.
</div>
</div>
</div>
</div>
</div>
<div class="row" ng-show="Features.BUILD_SUPPORT">
<div class="col-md-12">
<div class="section">
<div class="section-title">Initialize repository</div>
<div style="padding-top: 10px;">
<!-- Empty -->
<div class="repo-option">
<input type="radio" id="initEmpty" name="initialize" ng-model="repo.initialize" value="">
<i class="fa fa-hdd-o fa-lg" style="padding: 6px; padding-left: 8px; padding-right: 6px;"></i>
<label for="initEmpty" style="color: #aaa;">(Empty repository)</label>
</div>
<!-- Dockerfile -->
<div class="repo-option">
<input type="radio" id="initDockerfile" name="initialize" ng-model="repo.initialize" value="dockerfile">
<i class="fa fa-file fa-lg" style="padding: 6px; padding-left: 10px; padding-right: 8px;"></i>
<label for="initDockerfile">Initialize from a <b>Dockerfile</b></label>
</div>
<!-- Zip/TarGz file -->
<div class="repo-option">
<input type="radio" id="initZipfile" name="initialize" ng-model="repo.initialize" value="zipfile">
<i class="fa fa-archive fa-lg" style="padding: 6px; padding-left: 10px; padding-right: 8px;"></i>
<label for="initZipfile">Initialize from a <b>Dockerfile</b> inside a <code>.zip</code> or <code>.tar.gz</code> archive</label>
</div>
<!-- Github -->
<div class="repo-option" ng-show="Features.GITHUB_BUILD">
<input type="radio" id="initGithub" name="initialize" ng-model="repo.initialize" value="github">
<i class="fa fa-github fa-lg" style="padding: 6px; padding-left: 10px; padding-right: 12px;"></i>
<label for="initGithub">Link to a GitHub Repository</label>
</div>
</div>
</div>
</div>
</div>
<div class="row" ng-show="repo.initialize == 'dockerfile' || repo.initialize == 'zipfile'">
<div class="col-md-12">
<div class="section">
<div class="section-title">Upload <span ng-if="repo.initialize == 'dockerfile'">Dockerfile</span><span ng-if="repo.initialize == 'zipfile'">Archive</span></div>
<div style="padding-top: 20px;">
<div class="initialize-repo">
<div class="dockerfile-build-form" repository="createdForBuild || repo" upload-failed="handleBuildFailed(message)"
build-started="handleBuildStarted()" build-failed="handleBuildFailed(message)" start-now="createdForBuild"
is-ready="hasDockerfile" uploading="uploading" building="building"></div>
</div>
</div>
</div>
</div>
</div>
<div class="row" ng-show="repo.initialize == 'github'">
<div class="col-md-12">
<div class="alert alert-info">
You will be redirected to authorize via GitHub once the repository has been created
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<button class="btn btn-large btn-success" type="submit"
ng-disabled="uploading || building || newRepoForm.$invalid || (repo.is_public == '0' && (planRequired || checkingPlan)) || ((repo.initialize == 'dockerfile' || repo.initialize == 'zipfile') && !hasDockerfile)">
<i class="fa fa-large" ng-class="repo.is_public == '1' ? 'fa-unlock' : 'fa-lock'" style="margin-right: 4px"></i>
Create {{ repo.is_public == '1' ? 'Public' : 'Private' }} Repository
</button>
</div>
</div>
</form>
</div>
<!-- Modal edit for the description -->
<div class="modal fade" id="editModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Edit Repository Description</h4>
</div>
<div class="modal-body">
<div class="wmd-panel">
<div id="wmd-button-bar-description"></div>
<textarea class="wmd-input" id="wmd-input-description" placeholder="Enter description">{{ repo.description }}</textarea>
</div>
<div id="wmd-preview-description" class="wmd-panel wmd-preview"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" ng-click="saveDescription()">Save changes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="cannotcreateModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Cannot create repository</h4>
</div>
<div class="modal-body">
The repository could not be created.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="couldnotsubscribeModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Cannot upgrade plan</h4>
</div>
<div class="modal-body">
Your current plan could not be upgraded. Please try again.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View file

@ -1,86 +0,0 @@
<div class="resource-view" resource="orgResource" error-message="'No matching organization found'">
<div class="org-view cor-container">
<div class="organization-header" organization="organization">
<div class="header-buttons" ng-show="organization.is_admin">
<span class="popup-input-button" pattern="TEAM_PATTERN" placeholder="'Team Name'"
submitted="createTeam(value)">
<i class="fa fa-group"></i> Create Team
</span>
<a class="btn btn-default" href="/organization/{{ organization.name }}/admin"><i class="fa fa-gear"></i> Settings</a>
</div>
</div>
<div class="row hidden-xs">
<div class="col-md-4 col-md-offset-8 col-sm-5 col-sm-offset-7 header-col" ng-show="organization.is_admin">
Team Permissions
<i class="info-icon fa fa-info-circle" data-placement="bottom" data-original-title="" data-title=""
data-content="Global permissions for the team and its members<br><br><dl><dt>Member</dt><dd>Permissions are assigned on a per repository basis</dd><dt>Creator</dt><dd>A team can create its own repositories</dd><dt>Admin</dt><dd>A team has full control of the organization</dd></dl>"></i>
</div>
</div>
<div class="team-listing" ng-repeat="(name, team) in organization.teams">
<div id="team-{{name}}" class="row">
<div class="col-sm-7 col-md-8">
<div class="team-title">
<i class="fa fa-group"></i>
<span ng-show="team.can_view">
<a href="/organization/{{ organization.name }}/teams/{{ team.name }}">{{ team.name }}</a>
</span>
<span ng-show="!team.can_view">
{{ team.name }}
</span>
</div>
<div class="team-description markdown-view" content="team.description" first-line-only="true"></div>
</div>
<div class="col-sm-5 col-md-4 control-col" ng-show="organization.is_admin">
<span class="role-group" current-role="team.role" role-changed="setRole(role, team.name)"
roles="teamRoles"></span>
<button class="btn btn-sm btn-danger" ng-click="askDeleteTeam(team.name)">Delete</button>
</div>
</div>
</div>
</div>
</div>
<!-- Modal message dialog -->
<div class="modal fade" id="cannotChangeTeamModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Cannot change team</h4>
</div>
<div class="modal-body">
<span ng-show="!roleError">You do not have permission to change properties on teams.</span>
<span ng-show="roleError">{{ roleError }}</span>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="confirmdeleteModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Delete Team?</h4>
</div>
<div class="modal-body">
Are you sure you would like to delete this team? This <b>cannot be undone</b>.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" ng-click="deleteTeam()">Delete Team</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View file

@ -1,74 +0,0 @@
<div class="cor-container">
<div class="repo-list" ng-show="!user.anonymous">
<div ng-class="user.organizations.length ? 'section-header' : ''">
<div class="button-bar-right">
<a href="/new/">
<button class="btn btn-success">
<i class="fa fa-upload user-tool" data-title="Create new repository"></i>
Create Repository
</button>
</a>
<a href="/organization/{{ namespace }}" ng-show="namespace != user.username">
<button class="btn btn-default">
<i class="fa fa-group user-tool"></i>
View Organization
</button>
</a>
</div>
<span class="namespace-selector" user="user" namespace="namespace" ng-show="user.organizations"></span>
</div>
<h3 ng-show="namespace == user.username">Your Repositories</h3>
<h3 ng-show="namespace != user.username">Repositories</h3>
<div class="resource-view" resource="user_repositories">
<!-- User/Org has repositories -->
<div ng-show="user_repositories.value.length > 0">
<div class="repo-listing" ng-repeat="repository in user_repositories.value">
<span class="repo-circle no-background" repo="repository"></span>
<a ng-href="/repository/{{repository.namespace}}/{{ repository.name }}"
data-repo="{{repository.namespace}}/{{ repository.name }}">
{{repository.namespace}}/{{repository.name}}
</a>
<div class="description markdown-view" content="repository.description" first-line-only="true"></div>
</div>
</div>
<!-- User/Org has no repositories -->
<div ng-show="user_repositories.value.length == 0" style="padding:20px;">
<div class="alert alert-info">
<h4 ng-show="namespace == user.username">You don't have any repositories yet!</h4>
<h4 ng-show="namespace != user.username">This organization doesn't have any repositories, or you have not been provided access.</h4>
<a href="http://docs.quay.io/solution/getting-started.html"><b>Click here</b> to learn how to create a repository</a>
</div>
</div>
</div>
</div>
<div class="repo-list">
<h3>Top Public Repositories</h3>
<div class="resource-view" resource="public_repositories">
<div class="repo-listing" ng-repeat="repository in public_repositories.value">
<span class="repo-circle no-background" repo="repository"></span>
<a ng-href="/repository/{{repository.namespace}}/{{ repository.name }}"
data-repo="{{repository.namespace}}/{{ repository.name }}">
{{repository.namespace}}/{{repository.name}}
</a>
<div class="description markdown-view" content="repository.description" first-line-only="true"></div>
</div>
<div class="page-controls">
<button class="btn btn-default" data-title="Previous Page" bs-tooltip="title" ng-show="page > 1"
ng-click="movePublicPage(-1)">
<i class="fa fa-chevron-left"></i>
</button>
<button class="btn btn-default" data-title="Next Page" bs-tooltip="title" ng-show="page < publicPageCount"
ng-click="movePublicPage(1)">
<i class="fa fa-chevron-right"></i>
</button>
</div>
</div>
</div>
</div>

View file

@ -1,425 +0,0 @@
<div id="tagContextMenu" class="dropdown clearfix" tabindex="-1">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu">
<li><a tabindex="-1" href="javascript:void(0)" ng-click="askDeleteTag(currentMenuTag)">Delete Tag</a></li>
</ul>
</div>
<div class="resource-view" resource="repository" error-message="'No Repository Found'">
<div class="container-fluid repo repo-view">
<!-- Repo Header -->
<div class="header">
<h3>
<span class="repo-circle no-background" repo="repo"></span>
<span class="repo-breadcrumb" repo="repo"></span>
</h3>
<div class="repo-controls">
<!-- Builds -->
<div class="dropdown" data-placement="top" style="display: inline-block"
bs-tooltip=""
data-title="{{ runningBuilds.length ? 'Dockerfile Builds Running: ' + (runningBuilds.length) : 'Dockerfile Build' }}"
quay-show="Features.BUILD_SUPPORT && (repo.can_write || buildHistory.length)">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-tasks fa-lg"></i>
<span class="count" ng-class="runningBuilds.length ? 'visible' : ''"><span>{{ runningBuilds.length ? runningBuilds.length : '' }}</span></span>
<b class="caret"></b>
</button>
<ul class="dropdown-menu">
<li ng-show="repo.can_write"><a href="{{ '/repository/' + repo.namespace + '/' + repo.name + '/build' }}">
<i class="fa fa-tasks"></i>Dockerfile Build History</a>
</li>
<li ng-show="repo.can_write">
<a href="javascript:void(0)" ng-click="showNewBuildDialog()">
<i class="fa fa-plus" style="margin-left: 1px; margin-right: 8px;"></i>New Dockerfile Build
</a>
</li>
<li ng-show="repo.can_admin">
<a href="/repository/{{ repo.namespace }}/{{ repo.name }}/admin?tab=trigger">
<i class="fa fa-bolt" style="margin-left: 3px; margin-right: 10px;"></i>Build Triggers
</a>
</li>
<li role="presentation" class="divider" ng-show="buildHistory && repo.can_write"></li>
<li role="presentation" class="dropdown-header" ng-show="buildHistory.length">Recent Builds</li>
<li ng-repeat="buildInfo in buildHistory">
<div class="build-info" ng-class="repo.can_write ? 'clickable' : ''" ng-click="repo.can_write && showBuild(buildInfo)">
<span class="build-status" build="buildInfo"></span>
</div>
</li>
</ul>
</div>
<!-- Admin -->
<a id="admin-cog" href="{{ '/repository/' + repo.namespace + '/' + repo.name + '/admin' }}"
ng-show="repo.can_admin">
<button class="btn btn-default" data-title="Repository Settings" bs-tooltip="tooltip" data-placement="top">
<i class="fa fa-cog fa-lg"></i></button></a>
<!-- Pull Command -->
<span class="pull-command visible-md-inline">
<div class="pull-container" ng-show="currentPullCommand">
<button class="pull-selector dropdown-toggle" data-toggle="dropdown">
<i class="fa" ng-class="currentPullCommand.icon"></i>
{{ currentPullCommand.shortTitle }}
<b class="caret"></b>
</button>
<ul class="dropdown-menu">
<li ng-repeat="pullCommand in pullCommands">
<a href="javascript:void(0)" ng-click="setCurrentPullCommand(pullCommand)"><i class="fa" ng-class="pullCommand.icon"></i>
{{ pullCommand.title }}
<sup ng-if="pullCommand.experimental">Experimental</sup>
</a>
</li>
</ul>
<div class="copy-box" hovering-message="true" value="currentPullCommand.command"></div>
</div>
</span>
</div>
</div>
<!-- Description -->
<div class="description markdown-input" content="repo.description" can-write="repo.can_write"
content-changed="updateForDescription" field-title="'repository description'"></div>
<!-- Empty messages -->
<div ng-if="!currentTag.image_id && !currentImage">
<!-- !building && !pushing -->
<div class="repo-content" ng-show="!repo.is_building && !isPushing(images)">
<div class="empty-message">
This repository is empty
</div>
<div class="empty-description" ng-show="repo.can_write">
<div class="panel-default">
<div class="panel-heading">How to push a new image to this repository:</div>
<div class="panel-body">
First login to the registry (if you have not done so already):
<pre class="command">sudo docker login {{ Config.getDomain() }}</pre>
Tag an image to this repository:
<pre class="command">sudo docker tag <i>0u123imageidgoeshere</i> {{ Config.getDomain() }}/{{repo.namespace}}/{{repo.name}}</pre>
Push the image to this repository:
<pre class="command">sudo docker push {{ Config.getDomain() }}/{{repo.namespace}}/{{repo.name}}</pre>
</div>
</div>
</div>
</div>
<!-- building -->
<div class="repo-content" ng-show="repo.is_building">
<div class="empty-message">
A build is currently processing. If this takes longer than an hour, please <a href="/contact">contact us</a>
</div>
</div>
<!-- pushing -->
<div class="repo-content" ng-show="!repo.is_building && isPushing(images)">
<div class="empty-message">
A push to this repository is in progress.
</div>
</div>
</div>
<!-- Content view -->
<div class="repo-content" ng-show="currentTag.image_id || currentImage">
<!-- Image History -->
<div id="image-history">
<div class="row">
<!-- Tree View container -->
<div class="col-md-8">
<div class="panel panel-default">
<!-- Image history tree -->
<div class="resource-view" resource="imageHistory">
<div id="image-history-container" onresize="tree.notifyResized()"></div>
</div>
</div>
</div>
<!-- Side Panel -->
<div class="col-md-4">
<div id="side-panel" class="panel panel-default">
<div class="panel-heading">
<!-- Dropdown -->
<div id="side-panel-dropdown" class="tag-dropdown dropdown" data-placement="top">
<i class="fa fa-tag current-context-icon" ng-show="currentTag"></i>
<i class="fa fa-archive current-context-icon" ng-show="!currentTag"></i>
<a href="javascript:void(0)" class="dropdown-toggle" data-toggle="dropdown">
<span class="current-context">
{{currentTag ? currentTag.name : currentImage.id.substr(0, 12)}}
</span>
<b class="caret"></b></a>
<ul class="dropdown-menu">
<li ng-repeat="tag in repo.tags">
<a href="javascript:void(0)" ng-click="setTag(tag.name, true)">
<i class="fa fa-tag"></i>{{tag.name}}
</a>
</li>
<li class="divider"></li>
<li ng-repeat="image in imageHistory.value">
<a href="javascript:void(0)" ng-click="setImage(image.id, true)">
{{image.id.substr(0, 12)}}
</a>
</li>
</ul>
</div>
<span class="right-tag-controls">
<i class="fa fa-tag" data-title="Tags" bs-tooltip="title">
<span class="tag-count">{{getTagCount(repo)}}</span>
</i>
<i class="fa fa-archive" data-title="Images" bs-tooltip="title">
<span class="tag-count">{{imageHistory.value.length}}</span>
</i>
</span>
</div>
<div class="panel-body">
<!-- Current Tag -->
<div id="current-tag" ng-show="currentTag">
<dl class="dl-normal">
<dt>Last Modified</dt>
<dd ng-if="!findImageForTag(currentTag, images)">
<span class="quay-spinner"></span>
</dd>
<dd am-time-ago="parseDate(findImageForTag(currentTag, images).created)"
ng-if="findImageForTag(currentTag, images)">
</dd>
<dt>Total Compressed Size</dt>
<dd><span class="context-tooltip"
data-title="The amount of data sent between Docker and the registry when pushing/pulling"
bs-tooltip="tooltip.title" data-container="body">{{ getTotalSize(currentTag) | bytes }}</span>
</dd>
</dl>
<div class="tag-image-sizes">
<div class="tag-image-size" ng-repeat="image in getImagesForTagBySize(currentTag) | limitTo: 10">
<span class="size-limiter">
<span class="size-bar" style="{{ 'width:' + (image.size / getTotalSize(currentTag)) * 100 + '%' }}"
bs-tooltip="" data-title="{{ image.size | bytes }}"></span>
</span>
<span class="size-title">
<a class="image-size-link" href="javascript:void(0)" ng-click="setImage(image.id, true)"
data-image="{{ image.id.substr(0, 12) }}">
{{ image.id.substr(0, 12) }}
</a>
</span>
</div>
</div>
<div class="control-bar" ng-show="repo.can_admin">
<button class="btn btn-default" ng-click="askDeleteTag(currentTag.name)">
Delete Tag
</button>
</div>
</div>
<!-- Current Image -->
<div id="current-image" ng-show="currentImage && !currentTag">
<div class="image-comment" ng-if="currentImage.comment">
<blockquote style="margin-top: 10px;">
<span class="markdown-view" content="currentImage.comment"></span>
</blockquote>
</div>
<div class="image-section">
<i class="fa fa-code section-icon" bs-tooltip="tooltip.title" data-title="Full Image ID"></i>
<span class="section-info">
<a class="image-anchor" href="{{'/repository/' + repo.namespace + '/' + repo.name + '/image/' + currentImage.id}}">{{ currentImage.id }}</a>
</span>
</div>
<div class="image-section">
<i class="fa fa-tag section-icon" bs-tooltip="tooltip.title" data-title="Current Tags"></i>
<span class="section-info section-info-with-dropdown">
<a class="label tag label-default" ng-repeat="tag in currentImage.tags"
href="/repository/{{ repo.namespace }}/{{ repo.name }}?tag={{ tag }}">
{{ tag }}
</a>
<span style="color: #ccc;" ng-if="!currentImage.tags.length">(No Tags)</span>
<div class="dropdown" data-placement="top" ng-if="repo.can_write || currentImage.tags">
<a href="javascript:void(0)" class="dropdown-button" data-toggle="dropdown" bs-tooltip="tooltip.title" data-title="Manage Tags"
data-container="body">
<b class="caret"></b>
</a>
<ul class="dropdown-menu pull-right">
<li ng-repeat="tag in currentImage.tags">
<a href="javascript:void(0)" ng-click="setTag(tag, true)">
<i class="fa fa-tag"></i>{{tag}}
</a>
</li>
<li class="divider" role="presentation" ng-if="repo.can_write && currentImage.tags"></li>
<li>
<a href="javascript:void(0)" ng-click="showAddTag(currentImage)" ng-if="repo.can_write">
<i class="fa fa-plus"></i>Add New Tag
</a>
</li>
</ul>
</div>
</span>
</div>
<div class="image-section" ng-if="currentImage.command && currentImage.command.length">
<i class="fa fa-terminal section-icon" bs-tooltip="tooltip.title" data-title="Image Command"></i>
<span class="section-info">
<span class="formatted-command trimmed"
data-html="true"
bs-tooltip="" data-title="{{ getTooltipCommand(currentImage) }}"
data-placement="top">{{ getFormattedCommand(currentImage) }}</span>
</span>
</div>
<div class="image-section">
<i class="fa fa-calendar section-icon" bs-tooltip="tooltip.title" data-title="Created"></i>
<span class="section-info">
<dd am-time-ago="parseDate(currentImage.created)"></dd>
</span>
</div>
<div class="image-section">
<i class="fa fa-cloud-upload section-icon" bs-tooltip="tooltip.title"
data-title="The amount of data sent between Docker and the registry when pushing/pulling"></i>
<span class="section-info">{{ currentImage.size | bytes }}</span>
</div>
<div class="image-section">
<i class="fa fa-map-marker section-icon" bs-tooltip="tooltip.title"
data-title="The geographic region(s) in which this image data is located"></i>
<span class="section-info">
<span class="location-view" location="location" ng-repeat="location in currentImage.locations"></span>
</span>
</div>
<!-- Image changes loading -->
<div class="resource-view" resource="currentImageChangeResource">
<div class="changes-container small-changes-container section-info"
ng-show="currentImageChanges.changed.length || currentImageChanges.added.length || currentImageChanges.removed.length">
<div class="changes-count-container image-section">
<i class="fa fa-code-fork section-icon" bs-tooltip="tooltip.title" data-title="File Changes"></i>
<div style="float: right; display: inline-block">
<span class="change-count added" ng-show="currentImageChanges.added.length > 0" data-title="Files Added"
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
<i class="fa fa-plus-square"></i>
<b>{{currentImageChanges.added.length}}</b>
</span>
<span class="change-count removed" ng-show="currentImageChanges.removed.length > 0" data-title="Files Removed"
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
<i class="fa fa-minus-square"></i>
<b>{{currentImageChanges.removed.length}}</b>
</span>
<span class="change-count changed" ng-show="currentImageChanges.changed.length > 0" data-title="Files Changed"
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
<i class="fa fa-pencil-square"></i>
<b>{{currentImageChanges.changed.length}}</b>
</span>
</div>
<div id="collapseChanges" style="padding-top: 24px;">
<div class="well well-sm">
<div class="change added" ng-repeat="file in currentImageChanges.added | limitTo:2">
<i class="fa fa-plus-square"></i>
<span title="{{file}}">{{file}}</span>
</div>
<div class="change removed" ng-repeat="file in currentImageChanges.removed | limitTo:2">
<i class="fa fa-minus-square"></i>
<span title="{{file}}">{{file}}</span>
</div>
<div class="change changed" ng-repeat="file in currentImageChanges.changed | limitTo:2">
<i class="fa fa-pencil-square"></i>
<span title="{{file}}">{{file}}</span>
</div>
</div>
<div class="more-changes" ng-show="getMoreCount(currentImageChanges) > 0">
<a href="{{'/repository/' + repo.namespace + '/' + repo.name + '/image/' + currentImage.id}}">
And {{getMoreCount(currentImageChanges)}} more...
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="dockerfile-build-dialog" show-now="buildDialogShowCounter" repository="repo"
build-started="handleBuildStarted(build)">
</div>
<!-- Modal message dialog -->
<div class="modal fade" id="addTagModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-show="!creatingTag">&times;</button>
<h4 class="modal-title">{{ isAnotherImageTag(toTagImage, tagToCreate) ? 'Move' : 'Add' }} Tag to Image {{ toTagImage.id.substr(0, 12) }}</h4>
</div>
<form name="addTagForm" ng-submit="createOrMoveTag(toTagImage, tagToCreate, addTagForm.$invalid); addTagForm.$setPristine(); tagToCreate=''">
<div class="modal-body">
<input type="text" class="form-control" id="tagName" placeholder="Enter tag name"
ng-model="tagToCreate" ng-pattern="/^([a-z0-9_\.-]){3,30}$/" required
ng-disabled="creatingTag" autofocus>
<div style="margin: 10px; margin-top: 20px;" ng-show="isOwnedTag(toTagImage, tagToCreate)">
Note: <span class="label tag label-default">{{ tagToCreate }}</span> is already applied to this image.
</div>
<div style="margin: 10px; margin-top: 20px;" ng-show="isAnotherImageTag(toTagImage, tagToCreate)">
Note: <span class="label tag label-default">{{ tagToCreate }}</span> is already applied to another image. This will <b>move</b> the tag.
</div>
<div class="tag-specific-images-view" tag="tagToCreate" repository="repo" images="images" image-cutoff="toTagImage"
style="margin: 10px; margin-top: 20px; margin-bottom: -10px;" ng-show="isAnotherImageTag(toTagImage, tagToCreate)">
This will also delete any unattach images and delete the following images:
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary"
ng-disabled="!tagToCreate || addTagForm.$invalid || isOwnedTag(toTagImage, tagToCreate)"
ng-class="isAnotherImageTag(toTagImage, tagToCreate) ? 'btn-warning' : 'btn-primary'" ng-show="!creatingTag">
{{ isAnotherImageTag(toTagImage, tagToCreate) ? 'Move Tag' : 'Create Tag' }}
</button>
<button class="btn btn-default" data-dismiss="modal" ng-show="!creatingTag">Cancel</button>
<div class="quay-spinner" ng-show="creatingTag"></div>
</div>
</form>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="confirmdeleteTagModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Delete tag
<span class="label tag" ng-class="tagToDelete == currentTag.name ? 'label-success' : 'label-default'">
{{ tagToDelete }}
</span>?
</h4>
</div>
<div class="modal-body" ng-show="deletingTag">
<div class="quay-spinner"></div>
</div>
<div class="modal-body" ng-show="!deletingTag">
Are you sure you want to delete tag
<span class="label tag" ng-class="tagToDelete == currentTag.name ? 'label-success' : 'label-default'">
{{ tagToDelete }}
</span>?
<div class="tag-specific-images-view" tag="tagToDelete" repository="repo" images="images" style="margin-top: 20px">
The following images and any other images not referenced by a tag will be deleted:
</div>
</div>
<div class="modal-footer" ng-show="!deletingTag">
<button type="button" class="btn btn-primary" ng-click="deleteTag(tagToDelete)">Delete Tag</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View file

@ -1,128 +0,0 @@
<div class="resource-view" resource="orgResource" error-message="'No matching organization'">
<div class="team-view cor-container">
<div class="organization-header" organization="organization" team-name="teamname">
<div ng-show="canEditMembers" class="side-controls">
<div class="hidden-xs">
<button class="btn btn-success"
id="showAddMember"
data-title="Add Team Member"
data-content-template="/static/directives/team-view-add.html"
data-placement="bottom-right"
bs-popover="bs-popover">
<i class="fa fa-plus"></i>
Add Team Member
</button>
</div>
</div>
</div>
<div class="resource-view" resource="membersResource" error-message="'No matching team found'">
<div class="description markdown-input" content="team.description" can-write="organization.is_admin"
content-changed="updateForDescription" field-title="'team description'"></div>
<div class="empty-message" ng-if="!members.length">
This team has no members
</div>
<div class="empty-message" ng-if="members.length && !(members | filter:search).length">
No matching team members found
</div>
<table class="member-listing" style="margin-top: -20px" ng-show="members.length">
<!-- Members -->
<tr ng-if="(members | filter:search | filter: filterFunction(false, false)).length">
<td colspan="2"><div class="section-header">Team Members</div></td>
</tr>
<tr ng-repeat="member in members | filter:search | filter: filterFunction(false, false) | orderBy: 'name'">
<td class="user entity">
<span class="entity-reference" entity="member" namespace="organization.name" show-avatar="true" avatar-size="32"></span>
</td>
<td>
<span class="delete-ui" delete-title="'Remove ' + member.name + ' from team'" button-title="'Remove'"
perform-delete="removeMember(member.name)" ng-if="canEditMembers"></span>
</td>
</tr>
<!-- Robots -->
<tr ng-if="(members | filter:search | filter: filterFunction(false, true)).length">
<td colspan="2"><div class="section-header">Robot Accounts</div></td>
</tr>
<tr ng-repeat="member in members | filter:search | filter: filterFunction(false, true) | orderBy: 'name'">
<td class="user entity">
<span class="entity-reference" entity="member" namespace="organization.name"></span>
</td>
<td>
<span class="delete-ui" delete-title="'Remove ' + member.name + ' from team'" button-title="'Remove'"
perform-delete="removeMember(member.name)" ng-if="canEditMembers"></span>
</td>
</tr>
<!-- Invited -->
<tr ng-if="(members | filter:search | filter: filterFunction(true, false)).length">
<td colspan="2"><div class="section-header">Invited To Join</div></td>
</tr>
<tr ng-repeat="member in members | filter:search | filter: filterFunction(true, false) | orderBy: 'name'">
<td class="user entity">
<span ng-if="member.kind != 'invite'">
<span class="entity-reference" entity="member" namespace="organization.name" show-avatar="true" avatar-size="32"></span>
</span>
<span class="invite-listing" ng-if="member.kind == 'invite'">
<span class="avatar" size="32" data="member.avatar"></span>
{{ member.email }}
</span>
</td>
<td>
<span class="delete-ui" delete-title="'Revoke invite to join team'" button-title="'Revoke'"
perform-delete="revokeInvite(member)" ng-if="canEditMembers"></span>
</td>
</tr>
</table>
<div ng-show="canEditMembers">
<div ng-if-media="'(max-width: 767px)'">
<div ng-include="'/static/directives/team-view-add.html'"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal message dialog -->
<div class="modal fade" id="cannotChangeTeamModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Cannot change team</h4>
</div>
<div class="modal-body">
You do not have permission to change properties of this team.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="cannotChangeMembersModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Cannot change members</h4>
</div>
<div class="modal-body">
You do not have permission to change the members of this team.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View file

@ -1,3 +0,0 @@
<div class="cor-container">
<div class="angular-tour-ui" tour="tour" inline="true"></div>
</div>

View file

@ -1,127 +0,0 @@
<div class="resource-view" resource="orgResource" error-message="'No organization found'"></div>
<div class="org-admin cor-container" ng-show="organization">
<div class="organization-header" organization="organization" clickable="true"></div>
<div class="row">
<!-- Side tabs -->
<div class="col-md-2">
<ul class="nav nav-pills nav-stacked cor-tab-shim">
<li class="active" quay-require="['BILLING']">
<a href="javascript:void(0)" data-toggle="tab" data-target="#plan">Plan and Usage</a>
</li>
<li quay-classes="{'!Features.BILLING': 'active'}">
<a href="javascript:void(0)" data-toggle="tab" data-target="#settings">Organization Settings</a>
</li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">Usage Logs</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#members" ng-click="loadMembers()">Members</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#robots">Robot Accounts</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#prototypes">Default Permissions</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#applications" ng-click="loadApplications()">Applications</a></li>
<li ng-show="hasPaidPlan" quay-require="['BILLING']">
<a href="javascript:void(0)" data-toggle="tab" data-target="#billingoptions">Billing</a>
</li>
<li ng-show="hasPaidPlan" quay-require="['BILLING']">
<a href="javascript:void(0)" data-toggle="tab" data-target="#billing" ng-click="loadInvoices()">Billing History</a>
</li>
</ul>
</div>
<!-- Content -->
<div class="col-md-10">
<div class="tab-content">
<!-- Plans tab -->
<div id="plan" class="tab-pane active" quay-require="['BILLING']">
<div class="plan-manager" organization="orgname" plan-changed="planChanged(plan)"></div>
</div>
<!-- Organization settings tab -->
<div id="settings" class="tab-pane" quay-classes="{'!Features.BILLING': 'active'}">
<div class="quay-spinner" ng-show="changingOrganization"></div>
<div class="panel" ng-show="!changingOrganization">
<div class="panel-title">Organization's e-mail address</div>
<div class="panel-content" style="padding-left: 20px; margin-top: 10px;">
<form class="form-change" id="changeEmailForm" name="changeEmailForm" ng-submit="changeEmail()" data-trigger="manual"
data-content="{{ changeEmailError }}" data-placement="bottom" ng-show="!updatingOrganization">
<input type="email" class="form-control" ng-model="organizationEmail"
style="margin-left: 10px; margin-right: 10px; width: 400px; display: inline-block;" required>
<button class="btn btn-primary" type="submit" ng-disabled="changeEmailForm.$invalid || organizationEmail == organization.email">
Save
</button>
</form>
</div>
</div>
</div>
<!-- Robot accounts tab -->
<div id="robots" class="tab-pane">
<div class="robots-manager" organization="organization" is-enabled="true"></div>
</div>
<!-- Prototypes tab -->
<div id="prototypes" class="tab-pane">
<div class="prototype-manager" organization="organization"></div>
</div>
<!-- Logs tab -->
<div id="logs" class="tab-pane">
<div class="logs-view" organization="organization" makevisible="logsShown"></div>
</div>
<!-- Applications tab -->
<div id="applications" class="tab-pane">
<div class="application-manager" organization="organization" makevisible="applicationsShown"></div>
</div>
<!-- Billing Options tab -->
<div id="billingoptions" class="tab-pane" quay-require="['BILLING']">
<div class="billing-options" organization="organization"></div>
</div>
<!-- Billing History tab -->
<div id="billing" class="tab-pane" quay-require="['BILLING']">
<div class="billing-invoices" organization="organization" makevisible="invoicesShown"></div>
</div>
<!-- Members tab -->
<div id="members" class="tab-pane">
<div class="quay-spinner" ng-show="membersLoading"></div>
<div ng-show="!membersLoading">
<div class="side-controls">
<div class="result-count">
Showing {{(membersFound | filter:search | limitTo:50).length}} of {{(membersFound | filter:search).length}} matching members
</div>
<div class="filter-input">
<input id="member-filter" class="form-control" placeholder="Filter Members" type="text" ng-model="search.$">
</div>
</div>
<table class="table table-striped">
<thead>
<th>User/Robot Account</th>
<th>Teams</th>
<th></th>
</thead>
<tr ng-repeat="memberInfo in (membersFound | filter:search | limitTo:50)">
<td>
<span class="entity-reference" entity="memberInfo" namespace="organization.name"></span>
</td>
<td>
<span class="team-link" ng-repeat="team in memberInfo.teams">
<span class="entity-reference" namespace="organization.name" entity="{'kind': 'team', 'name': team}"></span>
</span>
</td>
<td>
<a href="/organization/{{ organization.name }}/logs/{{ memberInfo.name }}" data-title="Member Usage Logs" bs-tooltip="tooltip.title">
<i class="fa fa-book"></i>
</a>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,7 +0,0 @@
<div class="resource-view" resource="memberResource" error-message="'Member not found'">
<div class="org-member-logs cor-container">
<div class="organization-header" organization="organization" clickable="true"></div>
<div class="logs-view" organization="organization" performer="memberInfo"
makevisible="organization && memberInfo && ready"></div>
</div>
</div>

View file

@ -1,480 +0,0 @@
<div class="cor-container" ng-show="deleting"><div class="quay-spinner"></div></div>
<div class="resource-view" resource="repository" error-message="'No repository found'"></div>
<div class="cor-container repo repo-admin" ng-show="accessDenied">
You do not have permission to view this page
</div>
<div class="cor-container repo repo-admin" ng-show="repo && !deleting">
<div class="header row">
<a href="{{ '/repository/' + repo.namespace + '/' + repo.name }}" class="back"><i class="fa fa-chevron-left"></i></a>
<h3>
<span class="repo-circle no-background" repo="repo"></span>
<span class="repo-breadcrumb" repo="repo" subsection-icon="'fa-cog'"
subsection="'Admin'"></span>
</h3>
</div>
<div class="row">
<!-- Side tabs -->
<div class="col-md-2">
<ul class="nav nav-pills nav-stacked cor-tab-shim">
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#permissions">Permissions</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#trigger" ng-click="loadTriggers()"
quay-show="Features.BUILD_SUPPORT">Build Triggers</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#badge">Status Badge</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#notification" ng-click="loadNotifications()">Notifications</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#publicprivate">Public/Private</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#delete">Delete</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">Usage Logs</a></li>
</ul>
</div>
<!-- Content -->
<div class="col-md-10">
<div class="tab-content">
<!-- Logs tab -->
<div id="logs" class="tab-pane">
<div class="logs-view" repository="repo" makevisible="logsShown"></div>
</div>
<!-- Badge tab -->
<div id="badge" class="tab-pane">
<div class="panel panel-default">
<div class="panel-heading">Status Badge
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="Embeddable widget for displaying the status of the repository"></i>
</div>
<div class="panel-body">
<div class="alert alert-warning" ng-if="!repo.is_public">
Note: This repository is currently <b>private</b>. Publishing this badge will reveal the status information of your repository (and links may
not work for unregistered users).
</div>
<!-- Status Image -->
<a ng-href="/repository/{{ repo.namespace }}/{{ repo.name }}" ng-if="repo && repo.name">
<img ng-src="/repository/{{ repo.namespace }}/{{ repo.name }}/status?token={{ repo.status_token }}" data-title="Docker Repository on Quay.io">
</a>
<!-- Embed formats -->
<table style="margin-top: 20px; width: 600px;">
<thead>
<th style="width: 150px"></th>
<th></th>
</thead>
<tr>
<td>Image (SVG):</td>
<td>
<div class="copy-box" hovering-message="true" value="getBadgeFormat('svg', repo)"></div>
</td>
</tr>
<tr>
<td>Markdown:</td>
<td>
<div class="copy-box" hovering-message="true" value="getBadgeFormat('md', repo)"></div>
</td>
</tr>
<tr>
<td>AsciiDoc:</td>
<td>
<div class="copy-box" hovering-message="true" value="getBadgeFormat('asciidoc', repo)"></div>
</td>
</tr>
</table>
</div>
</div>
</div>
<!-- Permissions tab -->
<div id="permissions" class="tab-pane active">
<!-- User Access Permissions -->
<div class="panel panel-default">
<div class="panel-heading">Access Permissions
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="Allow any number of users, robot accounts or teams to read, write or administer this repository"></i>
</div>
<div class="panel-body">
<!-- Throbber -->
<span class="quay-spinner" ng-show="permissions.loading > 0"></span>
<table class="permissions" ng-show="permissions.loading <= 0">
<thead>
<tr>
<td style="min-width: 400px;">User<span ng-show="repo.is_organization">/Team</span>/Robot Account</td>
<td>Permissions</td>
<td style="width: 95px;"></td>
</tr>
</thead>
<!-- Team Permissions -->
<tr ng-repeat="(name, permission) in permissions['team']">
<td class="team entity">
<span class="entity-reference" namespace="repo.namespace"
entity="buildEntityForPermission(name, permission, 'team')">
</span>
</td>
<td class="user-permissions">
<span class="role-group" current-role="permission.role"
role-changed="setRole(role, name, 'team')"
roles="repoRoles"></span>
</td>
<td>
<span class="delete-ui" delete-title="'Delete Permission'" perform-delete="deleteRole(name, 'team')"></span>
</td>
</tr>
<!-- User Permissions -->
<tr ng-repeat="(name, permission) in permissions['user']">
<td class="{{ 'user entity ' + (permission.is_org_member ? '' : 'outside') }}">
<span class="entity-reference" namespace="repo.namespace"
entity="buildEntityForPermission(name, permission, 'user')">
</span>
</td>
<td class="user-permissions">
<div class="btn-group btn-group-sm">
<span class="role-group" current-role="permission.role"
role-changed="setRole(role, name, 'user')" roles="repoRoles"></span>
</div>
</td>
<td>
<span class="delete-ui" delete-title="'Delete Permission'" perform-delete="deleteRole(name, 'user')"></span>
</td>
</tr>
<tr>
<td id="add-entity-permission" colspan="2" class="admin-search">
<span class="entity-search" namespace="repo.namespace"
placeholder="'Add a ' + (repo.is_organization ? 'team or ' : '') + 'user...'"
entity-selected="addNewPermission(entity)"
current-entity="selectedEntity"
auto-clear="true"></span>
</td>
</tr>
</table>
</div>
</div>
<!-- Token Permissions -->
<div class="panel panel-default">
<div class="panel-heading">Access Token Permissions
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="Grant permissions to this repository by creating unique tokens that can be used without entering account passwords<br><br>To use in docker:<br><dl class='dl-horizontal'><dt>Username</dt><dd>$token</dd><dt>Password</dt><dd>(token value)</dd><dt>Email</dt><dd>(any value)</dd></dl>"></i>
</div>
<div class="panel-body">
<form name="createTokenForm" ng-submit="createToken()">
<table class="permissions">
<thead>
<tr>
<td style="min-width: 400px;">Token Description</td>
<td>Permissions</td>
<td></td>
</tr>
</thead>
<tr ng-repeat="(code, token) in tokens">
<td class="user token">
<i class="fa fa-key"></i>
<a ng-click="showToken(token.code)">{{ token.friendlyName }}</a>
</td>
<td class="user-permissions">
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-default" ng-click="changeTokenAccess(token.code, 'read')" ng-class="{read: 'active', write: ''}[token.role]">Read only</button>
<button type="button" class="btn btn-default" ng-click="changeTokenAccess(token.code, 'write')" ng-class="{read: '', write: 'active'}[token.role]">Write</button>
</div>
</td>
<td>
<span class="delete-ui" delete-title="'Delete Token'" perform-delete="deleteToken(token.code)"></span>
</td>
</tr>
<tr>
<td class="admin-search">
<input type="text" class="form-control" placeholder="New token description" ng-model="newTokenName" required>
</td>
<td class="admin-search">
<button type="submit" ng-disabled="createTokenForm.$invalid" class="btn btn-sm btn-default">Create</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<!-- Notification tab -->
<div id="notification" class="tab-pane">
<div class="panel panel-default">
<div class="panel-heading">Repository Notifications
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="Notifications to call to external services (web, email, etc) on repository events"></i>
</div>
<div class="panel-body">
<!-- Notifications list -->
<div class="resource-view" resource="notificationsResource" error-message="'Could not load notifications'">
<div class="empty" ng-if="!notifications.length">
There are no notifications defined for this repository
</div>
<div class="nonempty" ng-show="notifications.length">
<div class="external-notification-view" notification="notification" repository="repo"
notification-deleted="handleNotificationDeleted(notification)"
ng-repeat="notification in notifications"></div>
</div>
</div>
<!-- Right controls -->
<div class="right-controls">
<button class="btn btn-success" ng-click="showNewNotificationDialog()">
<i class="fa fa-paper-plane"></i>
New Notification
</button>
</div>
</div>
</div>
</div>
<!-- Triggers tab -->
<div id="trigger" class="tab-pane" quay-show="['BUILD_SUPPORT']">
<div class="panel panel-default">
<div class="panel-heading">Build Triggers
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="Triggers from various services (such as GitHub) which tell the repository to be built and updated."></i>
</div>
<div class="panel-body">
<!-- Resource view -->
<div class="resource-view" resource="triggersResource" error-message="'Could not load build triggers'">
<div ng-show="!triggers.length">No build triggers defined for this repository</div>
<table class="permissions" ng-show="triggers.length">
<thead>
<tr>
<td style="width: 562px;">Trigger</td>
<td style="width: 104px;"></td>
</tr>
</thead>
<tbody>
<tr ng-repeat="trigger in triggers">
<td>
<div ng-show="!trigger.is_active" style="color: #444;">
<span class="quay-spinner" style="vertical-align: middle; margin-right: 6px;"></span>
Setting up trigger
</div>
<div ng-show="trigger.is_active" class="trigger-description" trigger="trigger"></div>
<div class="trigger-pull-credentials" ng-if="trigger.is_active && trigger.pull_robot">
<span class="context-tooltip" data-title="The credentials used by the builder when pulling images" bs-tooltip>
Pull Credentials:
</span>
<span class="entity-reference" entity="trigger.pull_robot"></span>
</div>
</td>
<td style="white-space: nowrap;" valign="top">
<div class="dropdown" style="display: inline-block" ng-visible="trigger.is_active">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" data-title="Build History" bs-tooltip="tooltip.title" data-container="body"
ng-click="loadTriggerBuildHistory(trigger)">
<i class="fa fa-tasks"></i>
<b class="caret"></b>
</button>
<ul class="dropdown-menu dropdown-menu-right pull-right">
<li ng-show="trigger.$loadingHistory" style="text-align: center"><span class="quay-spinner" style="padding: 4px;"></span></li>
<li role="presentation" class="dropdown-header" ng-show="!trigger.$loadingHistory && !trigger.$builds.length">No builds have been triggered</li>
<li role="presentation" class="dropdown-header" ng-show="!trigger.$loadingHistory && trigger.$builds.length">Build History</li>
<li ng-repeat="buildInfo in trigger.$builds">
<div class="build-info clickable" ng-click="showBuild(buildInfo)">
<span class="build-status" build="buildInfo"></span>
</div>
</li>
</ul>
</div>
<div class="dropdown" style="display: inline-block">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" data-title="Trigger Settings" bs-tooltip="tooltip.title" data-container="body">
<i class="fa fa-cog"></i>
<b class="caret"></b>
</button>
<ul class="dropdown-menu dropdown-menu-right pull-right">
<li><a href="javascript:void(0)" ng-click="setupTrigger(trigger)" ng-show="!trigger.is_active"><i class="fa ci-robot"></i>Resume Setup</a></li>
<li><a href="javascript:void(0)" ng-click="startTrigger(trigger)" ng-show="trigger.is_active"><i class="fa fa-tasks"></i>Build Now</a></li>
<li><a href="javascript:void(0)" ng-click="deleteTrigger(trigger)"><i class="fa fa-times"></i>Delete Trigger</a></li>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
<!-- Right controls -->
<div class="right-controls">
<span ng-show="!Features.GITHUB_BUILD" class="pull-left">No build trigger types enabled.</span>
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" data-toggle="dropdown" ng-disabled="!Features.GITHUB_BUILD">
New Trigger
<b class="caret"></b>
</button>
<ul class="dropdown-menu dropdown-menu-right pull-right">
<li>
<a href="{{ TriggerService.getRedirectUrl('github', repo.namespace, repo.name) }}">
<i class="fa fa-github fa-lg"></i>
GitHub <span ng-if="KeyService.isEnterprise('github-trigger')">Enterprise</span> - Repository Push
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Public/private tab -->
<div id="publicprivate" class="tab-pane">
<!-- Public/Private -->
<div class="panel panel-default">
<div class="panel-body">
<div class="repo-access-state" ng-show="!repo.is_public">
<div class="state-icon"><i class="fa fa-lock"></i></div>
This repository is currently <b>private</b>. Only users on the permissions list may view and interact with it.
<div class="change-access">
<button class="btn btn-danger" ng-click="askChangeAccess('public')">Make Public</button>
</div>
</div>
<div class="repo-access-state" ng-show="repo.is_public">
<div class="state-icon"><i class="fa fa-unlock"></i></div>
This repository is currently <b>public</b> and is visible to all users, and may be pulled by all users.
<div class="change-access">
<button class="btn btn-danger" ng-click="askChangeAccess('private')">Make Private</button>
</div>
</div>
</div>
</div>
</div>
<!-- Delete tab -->
<div id="delete" class="tab-pane">
<!-- Delete Repo -->
<div class="panel panel-default">
<div class="panel-body">
<div class="repo-delete">
<div class="alert alert-danger">Deleting a repository <b>cannot be undone</b>. Here be dragons!</div>
<button class="btn btn-danger" ng-click="askDelete()">Delete Repository</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Auth dialog -->
<div class="docker-auth-dialog" username="'$token'" token="shownToken.code"
shown="!!shownToken" counter="shownTokenCounter">
<i class="fa fa-key"></i> {{ shownToken.friendlyName }}
</div>
<!-- Setup trigger dialog-->
<div class="setup-trigger-dialog" repository="repo"
trigger="currentSetupTrigger"
canceled="cancelSetupTrigger(trigger)"
counter="showTriggerSetupCounter"></div>
<!-- New notification dialog-->
<div class="create-external-notification-dialog" repository="repo"
counter="showNewNotificationCounter"
notification-created="handleNotificationCreated(notification)"></div>
<!-- Manual trigger dialog -->
<div class="manual-trigger-build-dialog" repository="repo"
trigger="currentStartTrigger"
counter="showManualBuildDialog"
start-build="startTrigger(trigger, parameters)"></div>
<!-- Modal message dialog -->
<div class="modal fade" id="makepublicModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Make Repository Public</h4>
</div>
<div class="modal-body">
<div class="alert alert-warning">
Warning: This will allow <b>anyone</b> to pull from this repository
</div>
Are you sure you want to make this repository public?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" ng-click="changeAccess('public')">Make Public</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="makeprivateModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Make Repository Private</h4>
</div>
<div class="modal-body">
<div class="alert alert-warning">
Warning: Only users on the permissions list will be able to access this repository.
</div>
Are you sure you want to make this repository private?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" ng-click="changeAccess('private')">Make Private</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="confirmdeleteModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Delete Repository?</h4>
</div>
<div class="modal-body">
Are you <b>absolutely, positively</b> sure you would like to delete this repository? This <b>cannot be undone</b>.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" ng-click="deleteRepo()">Delete Repository</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="confirmaddoutsideModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Add User?</h4>
</div>
<div class="modal-body">
The selected user is outside of your organization. Are you sure you want to grant the user access to this repository?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="grantRole()">Yes, I'm sure</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View file

@ -1,141 +0,0 @@
<div class="resource-view" resource="repository" error-message="'No repository found'"></div>
<div class="cor-container repo repo-build" ng-show="accessDenied">
You do not have permission to view this page
</div>
<div class="cor-container repo repo-build" ng-show="repo">
<div class="header row">
<a href="{{ '/repository/' + repo.namespace + '/' + repo.name }}" class="back"><i class="fa fa-chevron-left"></i></a>
<h3>
<span class="repo-circle no-background" repo="repo"></span>
<span class="repo-breadcrumb" repo="repo" subsection-icon="'fa-tasks'" subsection="'Build History'"></span>
</h3>
<div class="repo-controls">
<button class="btn btn-success" ng-click="showNewBuildDialog()">
<i class="fa fa-plus"></i>
New Dockerfile Build
</button>
</div>
</div>
<div class="row" ng-show="!builds">
<div class="quay-spinner"></div>
</div>
<div class="row" ng-show="builds && !builds.length">
There are no builds for this repository
</div>
<div class="row" ng-show="builds.length">
<!-- Side tabs -->
<div class="col-sm-2">
<ul class="nav nav-pills nav-stacked cor-tab-shim">
<li ng-class="currentBuild == build ? 'active' : ''" ng-repeat="build in builds">
<a class="build-tab-link" href="javascript:void(0)" ng-click="setCurrentBuild(build.id, true)">
<span class="phase-icon" ng-class="build.phase"></span>
<span>{{ build.display_name }}</span>
</a>
</li>
</ul>
</div>
<!-- Content -->
<div class="col-sm-10">
<div class="tab-content" onresize="adjustLogHeight()">
<div ng-repeat="build in builds" class="tab-pane build-pane" ng-class="currentBuild == build ? 'active' : ''">
<div class="alert alert-info" ng-show="build.trigger">
Triggered by: <span class="trigger-description" trigger="build.trigger" short="true" style="margin-left: 10px"></span>
</div>
<div class="build-header">
<div class="timing">
<i class="fa fa-clock-o"></i>
Started: <span am-time-ago="build.started || 0"></span>
<span style="display: inline-block; margin-left: 20px" ng-show="currentBuild.resource_key">
<i class="fa fa-archive"></i>
<a href="/repository/{{ repo.namespace }}/{{ repo.name }}/build/{{ currentBuild.id }}/buildpack"
style="display: inline-block; margin-left: 6px" bs-tooltip="tooltip.title"
data-title="View the uploaded build package for this build">Build Package</a>
</span>
</div>
<span class="phase-icon" ng-class="build.phase"></span>
<span class="build-message" phase="build.phase"></span>
<div class="build-progress" build="build"></div>
</div>
<div class="build-logs">
<div ng-show="!logEntries">
<span class="quay-spinner"></span>
</div>
<div class="log-container" ng-class="container.type" ng-repeat="container in logEntries">
<div class="container-header" ng-class="container.type == 'phase' ? container.message : ''"
ng-switch on="container.type" ng-click="container.logs.toggle()">
<i class="fa chevron"
ng-class="container.logs.isVisible ? 'fa-chevron-down' : 'fa-chevron-right'" ng-show="hasLogs(container)"></i>
<div ng-switch-when="phase">
<span class="container-content build-log-phase" phase="container"></span>
</div>
<div ng-switch-when="error">
<span class="container-content build-log-error" error="container" entries="logEntries"></span>
</div>
<div ng-switch-when="command">
<span class="container-content build-log-command" command="container"></span>
</div>
</div>
<!-- Display the entries for the container -->
<div class="container-logs" ng-show="container.logs.isVisible">
<div class="log-entry" bindonce ng-repeat="entry in container.logs.visibleEntries">
<span class="id" bo-text="$index + container.index + 1"></span>
<span class="message" bo-html="processANSI(entry.message, container)"></span>
</div>
</div>
</div>
</div>
<div style="margin-top: 10px">
<button class="btn btn-default"
ng-show="build.phase == 'waiting' && build.resource_key"
ng-click="askCancelBuild(build)">
<i class="fa fa-times-circle" style="margin-right: 6px; display: inline-block;"></i>
Cancel Build
</button>
<button class="btn" ng-show="(build.phase == 'error' || build.phase == 'complete') && build.resource_key"
ng-class="build.phase == 'error' ? 'btn-success' : 'btn-default'"
ng-click="askRestartBuild(build)">
<i class="fa fa-refresh"></i>
Run Build Again
</button>
<span class="quay-spinner" ng-show="pollChannel.working"></span>
<span class="build-id">{{ build.id }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="dockerfile-build-dialog" show-now="buildDialogShowCounter" repository="repo"
build-started="handleBuildStarted(build)">
</div>
<!-- Modal message dialog -->
<div class="modal fade" id="confirmRestartBuildModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Run Dockerfile Build?</h4>
</div>
<div class="modal-body">
Are you sure you want to run this Dockerfile build again? The results will be immediately pushed to the repository.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="restartBuild(currentBuild)">Run Build</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</div>

View file

@ -1,436 +0,0 @@
<div class="loading" ng-show="!user">
<div class="quay-spinner"></div>
</div>
<div class="loading" ng-show="user.anonymous">
No matching user found
</div>
<div class="user-admin cor-container" ng-show="!user.anonymous">
<div class="row">
<div class="organization-header-element">
<span class="avatar" size="24" data="user.avatar"></span>
<span class="organization-name">
{{ user.username }}
</span>
</div>
</div>
<div class="row">
<!-- Side tabs -->
<div class="col-md-2">
<ul class="nav nav-pills nav-stacked cor-tab-shim">
<!-- Billing Related -->
<li class="active" quay-require="['BILLING']"><a href="javascript:void(0)" data-toggle="tab" data-target="#plan">Plan and Usage</a></li>
<li ng-show="hasPaidPlan" quay-require="['BILLING']">
<a href="javascript:void(0)" data-toggle="tab" data-target="#billingoptions">Billing Options</a>
</li>
<li ng-show="hasPaidPlan" quay-require="['BILLING']">
<a href="javascript:void(0)" data-toggle="tab" data-target="#billing" ng-click="loadInvoices()">Billing History</a>
</li>
<!-- Non-billing -->
<li quay-classes="{'!Features.BILLING': 'active'}"><a href="javascript:void(0)" data-toggle="tab" data-target="#email">Account E-mail</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#robots">Robot Accounts</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#password">Password</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#external" quay-show="Features.GITHUB_LOGIN || Features.GOOGLE_LOGIN">External Logins</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#authorized" ng-click="loadAuthedApps()">Authorized Applications</a></li>
<li quay-show="Features.USER_LOG_ACCESS || hasPaidBusinessPlan">
<a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">Usage Logs</a>
</li>
<li quay-require="['USER_RENAME']">
<a href="javascript:void(0)" data-toggle="tab" data-target="#username">Change Username</a>
</li>
<li quay-show="Config.AUTHENTICATION_TYPE == 'Database'">
<a href="javascript:void(0)" data-toggle="tab" data-target="#migrate" id="migrateTab">Convert to Organization</a>
</li>
</ul>
</div>
<!-- Content -->
<div class="col-md-10">
<div class="tab-content">
<!-- Authorized applications tab -->
<div id="authorized" class="tab-pane">
<div class="quay-spinner" ng-show="!authorizedApps"></div>
<div class="panel" ng-show="authorizedApps != null">
<div class="panel-body" ng-show="!authorizedApps.length">
You have not authorized any external applications
</div>
<div class="panel-body" ng-show="authorizedApps.length">
<div class="alert alert-info">
These are the applications you have authorized to view information and perform actions on your behalf.
</div>
<table class="table">
<thead>
<th>Application Name</th>
<th>Authorized Permissions</th>
<th style="width: 150px">Revoke</th>
</thead>
<tr class="auth-info" ng-repeat="authInfo in authorizedApps">
<td>
<span class="avatar" size="16" data="authInfo.application.avatar"></span>
<a href="{{ authInfo.application.url }}" ng-if="authInfo.application.url" target="_blank"
data-title="{{ authInfo.application.description || authInfo.application.name }}" bs-tooltip>
{{ authInfo.application.name }}
</a>
<span ng-if="!authInfo.application.url" data-title="{{ authInfo.application.description || authInfo.application.name }}" bs-tooltip>
{{ authInfo.application.name }}
</span>
<span class="by">{{ authInfo.application.organization.name }}</span>
</td>
<td>
<span class="label label-default scope"
ng-class="{'repo:admin': 'label-primary', 'repo:write': 'label-success', 'repo:create': 'label-success'}[scopeInfo.scope]"
ng-repeat="scopeInfo in authInfo.scopes" data-title="{{ scopeInfo.description }}" bs-tooltip>
{{ scopeInfo.scope }}
</span>
</td>
<td>
<span class="delete-ui" delete-title="'Revoke Authorization'" button-title="'Revoke'" perform-delete="deleteAccess(authInfo)"></span>
</td>
</tr>
</table>
</div>
</div>
</div>
<!-- Logs tab -->
<div id="logs" class="tab-pane">
<div class="logs-view" user="user" makevisible="logsShown"></div>
</div>
<!-- Plans tab -->
<div id="plan" class="tab-pane active" quay-require="['BILLING']">
<div class="plan-manager" user="user.username" ready-for-plan="readyForPlan()" plan-changed="planChanged(plan)"></div>
</div>
<!-- E-mail address tab -->
<div id="email" class="tab-pane" quay-classes="{'!Features.BILLING': 'active'}">
<div class="row">
<div class="alert alert-success" ng-show="changeEmailSent">An e-mail has been sent to {{ sentEmail }} to verify the change.</div>
<div class="loading" ng-show="updatingUser">
<div class="quay-spinner 3x"></div>
</div>
<div class="panel" ng-show="!updatingUser">
<div class="panel-title">Account e-mail address</div>
<div class="panel-setting-content panel-body">
{{ user.email }}
</div>
</div>
<div class="panel" ng-show="!updatingUser" quay-show="Features.MAILING">
<div class="panel-title">Change e-mail address</div>
<div class="panel-body">
<form class="form-change col-md-6" id="changeEmailForm" name="changeEmailForm" ng-submit="changeEmail()"
ng-show="!awaitingConfirmation && !registering">
<input type="email" class="form-control" placeholder="Your new e-mail address" ng-model="cuser.email" required>
<button class="btn btn-primary" ng-disabled="changeEmailForm.$invalid || cuser.email == user.email" type="submit">Change E-mail Address</button>
</form>
</div>
</div>
</div>
</div>
<!-- Password tab -->
<div id="password" class="tab-pane">
<!-- Encrypted Password -->
<div class="row">
<div class="panel">
<div class="panel-title">Generate Encrypted Password</div>
<div class="panel-body">
<div class="alert alert-info" ng-if="!Features.REQUIRE_ENCRYPTED_BASIC_AUTH">
Due to Docker storing passwords entered on the command line in <strong>plaintext</strong>, it is highly recommended to use the button below to generate an an encrypted version of your password.
</div>
<div class="alert alert-warning" ng-if="Features.REQUIRE_ENCRYPTED_BASIC_AUTH">
This installation is set to <strong>require</strong> encrypted passwords when
using the Docker command line interface. To generate an encrypted password, click the button below.
</div>
<button class="btn btn-primary" ng-click="generateClientToken()">
<i class="fa fa-key" style="margin-right: 6px;"></i>Generate Encrypted Password
</button>
</div>
</div>
</div>
<!-- Change Password -->
<div class="row">
<div class="panel">
<div class="panel-title">Change Password</div>
<div class="loading" ng-show="updatingUser">
<div class="quay-spinner 3x"></div>
</div>
<span class="help-block" ng-show="changePasswordSuccess">Password changed successfully</span>
<div ng-show="!updatingUser" class="panel-body">
<div class="alert alert-warning">Note: Changing your password will also invalidate any generated encrypted passwords.</div>
<form class="form-change col-md-6" id="changePasswordForm" name="changePasswordForm" ng-submit="changePassword()"
ng-show="!awaitingConfirmation && !registering">
<input type="password" class="form-control" placeholder="Your new password" ng-model="cuser.password" required
ng-pattern="/^.{8,}$/">
<input type="password" class="form-control" placeholder="Verify your new password" ng-model="cuser.repeatPassword"
match="cuser.password" required ng-pattern="/^.{8,}$/">
<button class="btn btn-danger" ng-disabled="changePasswordForm.$invalid" type="submit"
analytics-on analytics-event="change_pass">Change Password</button>
</form>
</div>
</div>
</div>
</div>
<!-- External Login tab -->
<div id="external" class="tab-pane" quay-show="Features.GITHUB_LOGIN || Features.GOOGLE_LOGIN">
<div class="loading" ng-show="!cuser">
<div class="quay-spinner 3x"></div>
</div>
<!-- Github -->
<div class="row" quay-show="cuser && Features.GITHUB_LOGIN">
<div class="panel">
<div class="panel-title">GitHub Login:</div>
<div class="panel-body">
<div ng-show="hasGithubLogin && githubLogin" class="lead col-md-8">
<i class="fa fa-github fa-lg" style="margin-right: 6px;" data-title="GitHub" bs-tooltip="tooltip.title"></i>
<b><a href="{{githubEndpoint}}{{githubLogin}}" target="_blank">{{githubLogin}}</a></b>
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
perform-delete="detachExternalLogin('github')"></span>
</div>
<div ng-show="hasGithubLogin && !githubLogin" class="lead col-md-8">
<i class="fa fa-github fa-lg" style="margin-right: 6px;" data-title="GitHub" bs-tooltip="tooltip.title"></i>
Account attached to Github Account
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
perform-delete="detachExternalLogin('github')"></span>
</div>
<div ng-show="!hasGithubLogin" class="col-md-4">
<span class="external-login-button" provider="github" action="attach"></span>
</div>
</div>
</div>
</div>
<!-- Google -->
<div class="row" quay-show="cuser && Features.GOOGLE_LOGIN">
<div class="panel">
<div class="panel-title">Google Login:</div>
<div class="panel-body">
<div ng-show="hasGoogleLogin && googleLogin" class="lead col-md-8">
<i class="fa fa-google fa-lg" style="margin-right: 6px;" data-title="Google" bs-tooltip="tooltip.title"></i>
<b>{{ googleLogin }}</b>
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
perform-delete="detachExternalLogin('google')"></span>
</div>
<div ng-show="hasGoogleLogin && !googleLogin" class="lead col-md-8">
<i class="fa fa-google fa-lg" style="margin-right: 6px;" data-title="Google" bs-tooltip="tooltip.title"></i>
Account attached to Google Account
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
perform-delete="detachExternalLogin('google')"></span>
</div>
<div ng-show="!hasGoogleLogin" class="col-md-4">
<span class="external-login-button" provider="google" action="attach"></span>
</div>
</div>
</div>
</div>
</div>
<!-- Robot accounts tab -->
<div id="robots" class="tab-pane">
<div class="robots-manager" user="user" is-enabled="true"></div>
</div>
<!-- Billing options tab -->
<div id="billingoptions" class="tab-pane" quay-require="['BILLING']">
<div class="billing-options" user="user"></div>
</div>
<!-- Billing History tab -->
<div id="billing" class="tab-pane" quay-require="['BILLING']">
<div class="billing-invoices" user="user" makevisible="invoicesShown"></div>
</div>
<!-- Change username tab -->
<div id="username" class="tab-pane" quay-show="Features.USER_RENAME">
<div class="row">
<div class="panel">
<div class="panel-title">Change Username</div>
<div class="loading" ng-show="updatingUser">
<div class="quay-spinner 3x"></div>
</div>
<span class="help-block" ng-show="changeUsernameSuccess">Username changed successfully</span>
<div ng-show="!updatingUser" class="panel-body">
<form class="form-change col-md-6" id="changeUsernameForm" name="changeUsernameForm" ng-submit="changePassword()"
ng-show="!awaitingConfirmation && !registering">
<input type="text" class="form-control" placeholder="Your new username" ng-model="cuser.username" required
ng-pattern="/{{ USER_PATTERN }}/">
<button class="btn btn-danger" ng-disabled="changeUsernameForm.$invalid" type="submit"
analytics-on analytics-event="change_username">Change Username</button>
</form>
</div>
</div>
</div>
</div>
<!-- Convert to organization tab -->
<div id="migrate" class="tab-pane" quay-show="Config.AUTHENTICATION_TYPE == 'Database'">
<!-- Step 0 -->
<div class="panel" ng-show="convertStep == 0">
<div class="panel-body" ng-show="user.organizations.length > 0">
<div class="alert alert-info">
Cannot convert this account into an organization, as it is a member of {{user.organizations.length}} other
organization{{user.organizations.length > 1 ? 's' : ''}}. Please leave
{{user.organizations.length > 1 ? 'those organizations' : 'that organization'}} first.
</div>
</div>
<div class="panel-body" ng-show="user.organizations.length == 0">
<div class="alert alert-warning">
Note: Converting a user account into an organization <b>cannot be undone</b>
</div>
<button class="btn btn-primary" ng-click="showConvertForm()">Start conversion process</button>
</div>
</div>
<!-- Step 1 -->
<div class="convert-form" ng-show="convertStep == 1">
<h3>Convert to organization</h3>
<form method="post" name="convertForm" id="convertForm" ng-submit="convertToOrg()">
<div class="form-group">
<label for="orgName">Organization Name</label>
<div class="existing-data">
<span class="avatar" size="24" data="user.avatar"></span>
{{ user.username }}</div>
<span class="description">This will continue to be the namespace for your repositories</span>
</div>
<div class="form-group">
<label for="orgName">Admin User</label>
<input id="adminUsername" name="adminUsername" type="text" class="form-control" placeholder="Admin Username"
ng-model="org.adminUser" required autofocus>
<input id="adminPassword" name="adminPassword" type="password" class="form-control" placeholder="Admin Password"
ng-model="org.adminPassword" required>
<span class="description">
The username and password for the account that will become an administrator of the organization.
Note that this account <b>must be a separate registered account</b> from the account that you are
trying to convert, and <b>must already exist</b>.
</span>
</div>
<!-- Plans Table -->
<div class="form-group plan-group" quay-require="['BILLING']">
<label>Organization Plan</label>
<div class="plans-table" plans="orgPlans" current-plan="org.plan"></div>
</div>
<div class="button-bar">
<button class="btn btn-large btn-danger" type="submit"
ng-disabled="convertForm.$invalid || (Features.BILLING && !org.plan)"
analytics-on analytics-event="convert_to_organization">
Convert To Organization
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal message dialog -->
<div class="modal fade" id="cannotconvertModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Cannot convert account</h4>
</div>
<div class="modal-body">
Your account could not be converted. Please try again in a moment.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="invalidadminModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Username or password invalid</h4>
</div>
<div class="modal-body">
The username or password specified for the admin account is not valid.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="clientTokenModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Encrypted Password</h4>
</div>
<div class="modal-body">
<div style="margin-bottom: 10px;">Your generated encrypted password:</div>
<div class="copy-box" value="generatedClientToken"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Dismiss</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="reallyconvertModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Convert to organization?</h4>
</div>
<div class="modal-body">
<div class="alert alert-danger">You will not be able to login to this account once converted</div>
<div>Are you <b>absolutely sure</b> you would like to convert this account to an organization? Once done, there is no going back.</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal" ng-click="reallyConvert()">Absolutely: Convert Now</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->