diff options
56 files changed, 8656 insertions, 54 deletions
diff --git a/3rd_party/static/onap-ui/app.js b/3rd_party/static/onap-ui/app.js new file mode 100644 index 0000000..e2f1364 --- /dev/null +++ b/3rd_party/static/onap-ui/app.js @@ -0,0 +1,202 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + /** Main app module where application dependencies are listed. */ + angular + .module('testapiApp', [ + 'ui.router','ui.bootstrap', 'cgBusy', + 'ngResource', 'angular-confirm', 'ngDialog', 'xeditable' + ]); + + angular + .module('testapiApp') + .config(configureRoutes); + + configureRoutes.$inject = ['$stateProvider', '$urlRouterProvider']; + + /** + * Handle application routing. Specific templates and controllers will be + * used based on the URL route. + */ + function configureRoutes($stateProvider, $urlRouterProvider) { + $urlRouterProvider.otherwise('/'); + $stateProvider. + state('home', { + url: '/', + templateUrl: 'onap-ui/components/home/home.html', + controller: 'HomeController as ctrl' + }). + state('directory', { + url: '/directory/:companyID&:logo', + templateUrl: 'onap-ui/components/directory/directory.html', + controller: 'DirectoryController as ctrl' + }). + state('userResults', { + url: '/user_results', + templateUrl: 'onap-ui/components/results/results.html', + controller: 'ResultsController as ctrl' + }). + state('communityResults', { + url: '/community_results', + templateUrl: 'onap-ui/components/results/results.html', + controller: 'ResultsController as ctrl' + }). + state('resultsDetail', { + url: '/results/:testID&:innerID', + templateUrl: 'onap-ui/components/results-report' + + '/resultsReport.html', + controller: 'ResultsReportController as ctrl' + }). + state('profile', { + url: '/profile', + templateUrl: 'onap-ui/components/profile/profile.html', + controller: 'ProfileController as ctrl' + }). + state('authFailure', { + url: '/auth_failure', + templateUrl: 'onap-ui/components/home/home.html', + controller: 'AuthFailureController as ctrl' + }). + state('logout', { + url: '/logout', + templateUrl: 'onap-ui/components/logout/logout.html', + controller: 'LogoutController as ctrl' + }). + state('application', { + url: '/application', + templateUrl: '/onap-ui/components/application/application.html', + controller: 'ApplicationController as ctrl' + }); + } + + angular + .module('testapiApp') + .config(disableHttpCache); + + disableHttpCache.$inject = ['$httpProvider']; + + /** + * Disable caching in $http requests. This is primarily for IE, as it + * tends to cache Angular IE requests. + */ + function disableHttpCache($httpProvider) { + if (!$httpProvider.defaults.headers.get) { + $httpProvider.defaults.headers.get = {}; + } + $httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache'; + $httpProvider.defaults.headers.get.Pragma = 'no-cache'; + } + + angular + .module('testapiApp') + .run(setup); + + setup.$inject = [ + '$http', '$rootScope', '$window', '$state', 'testapiApiUrl' + ]; + + /** + * Set up the app with injections into $rootscope. This is mainly for auth + * functions. + */ + function setup($http, $rootScope, $window, $state, testapiApiUrl) { + + $rootScope.auth = {}; + $rootScope.auth.doSignIn = doSignIn; + $rootScope.auth.doSignOut = doSignOut; + $rootScope.auth.doSignCheck = doSignCheck; + $rootScope.auth.canReview = canReview; + + + var sign_in_url = testapiApiUrl + '/auth/signin'; + var sign_out_url = testapiApiUrl + '/auth/signout'; + var profile_url = testapiApiUrl + '/profile'; + + function canReview(user) { + if (user.role.indexOf('reviewer') != -1) { + return true; + } else { + return false; + } + } + + /** This function initiates a sign in. */ + function doSignIn(type) { + $rootScope.auth.type = type; + $window.location.href = sign_in_url+"?type="+type; + } + + /** This function will initate a sign out. */ + function doSignOut() { + var resp = confirm("Are you sure to sign out?"); + if (!resp) + return; + $rootScope.auth.currentUser = null; + $rootScope.auth.isAuthenticated = false; + $window.location.href = sign_out_url+"?type="+$rootScope.auth.type; + } + + /** + * This function checks to see if a user is logged in and + * authenticated. + */ + function doSignCheck() { + return $http.get(profile_url, {withCredentials: true}). + success(function (data) { + $rootScope.auth.currentUser = data; + $rootScope.auth.isAuthenticated = true; + $rootScope.auth.type = data.type; + }). + error(function () { + $rootScope.auth.currentUser = null; + $rootScope.auth.isAuthenticated = false; + }); + } + + $rootScope.auth.doSignCheck(); + } + + angular + .element(document) + .ready(loadConfig); + + /** + * Load config and start up the angular application. + */ + function loadConfig() { + + var $http = angular.injector(['ng']).get('$http'); + + /** + * Store config variables as constants, and start the app. + */ + function startApp(config) { + // Add config options as constants. + angular.forEach(config, function(value, key) { + angular.module('testapiApp').constant(key, value); + }); + + angular.bootstrap(document, ['testapiApp']); + } + + $http.get('onap-ui/config.json').success(function (data) { + startApp(data); + }).error(function () { + startApp({}); + }); + } +})(); diff --git a/3rd_party/static/onap-ui/assets/css/ascend.css b/3rd_party/static/onap-ui/assets/css/ascend.css new file mode 100644 index 0000000..cd7370f --- /dev/null +++ b/3rd_party/static/onap-ui/assets/css/ascend.css @@ -0,0 +1,1170 @@ +.ascend .container-wrap,
+.ascend .project-title,
+body .vc_text_separator div,
+.carousel-wrap[data-full-width="true"] .carousel-heading,
+.carousel-wrap span.left-border,
+.carousel-wrap span.right-border,
+#page-header-wrap,
+.page-header-no-bg,
+#full_width_portfolio .project-title.parallax-effect,
+.portfolio-items .col,
+.page-template-template-portfolio-php .portfolio-items .col.span_3,
+.page-template-template-portfolio-php .portfolio-items .col.span_4 {
+ background-color: #f6f6f6;
+}
+
+#call-to-action .triangle {
+ color: #f6f6f6;
+}
+
+.ascend #footer-outer #footer-widgets .col ul li,
+.ascend #sidebar div ul li,
+.ascend #sidebar .widget.widget_categories li,
+.ascend #sidebar .widget.widget_pages li,
+.ascend #sidebar .widget.widget_nav_menu li {
+ border: none !important;
+ padding: 4px 0;
+}
+
+.ascend #sidebar .widget.widget_categories li,
+.ascend #sidebar .widget.widget_pages li,
+.ascend #sidebar .widget.widget_nav_menu li,
+.ascend #footer-outer .widget.widget_categories li,
+.ascend #footer-outer .widget.widget_pages li,
+.ascend #footer-outer .widget.widget_nav_menu li {
+ padding: 4px 0 !important;
+}
+
+.ascend #sidebar .widget.widget_categories li a,
+.ascend #sidebar .widget.widget_pages li a,
+.ascend #sidebar .widget.widget_nav_menu li a,
+.ascend #footer-outer .widget.widget_categories li a,
+.ascend #footer-outer .widget.widget_pages li a,
+.ascend #footer-outer .widget.widget_nav_menu li a {
+ padding: 0 !important;
+ border: none !important;
+}
+
+.ascend.woocommerce #sidebar div ul li {
+ padding: 6px 0 !important;
+}
+
+.ascend #footer-outer #footer-widgets .col ul li a,
+.ascend #sidebar div ul li a {
+ display: block;
+}
+
+.ascend #footer-outer .widget h4,
+.ascend #sidebar h4 {
+ margin-bottom: 12px;
+}
+
+.ascend #footer-outer #copyright {
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+ background-color: transparent;
+}
+
+.ascend #footer-outer[data-using-widget-area="false"] #copyright {
+ border: none;
+}
+
+.nectar-progress-bar span strong {
+ padding: 4px 0 !important;
+ background-color: transparent;
+ color: inherit;
+}
+
+.ascend {
+ background-color: #fff;
+}
+
+.nectar-progress-bar span strong:after {
+ display: none;
+}
+
+.nectar-progress-bar .bar-wrap {
+ background-color: rgba(0, 0, 0, 0.043);
+}
+
+.ascend .nectar-progress-bar .bar-wrap,
+.ascend .nectar-progress-bar span {
+ -webkit-box-shadow: none;
+ -o-box-shadow: none;
+ box-shadow: none;
+ -webkit-border-radius: 0;
+ -o-border-radius: 0;
+ border-radius: 0;
+}
+
+body .carousel-wrap[data-full-width="false"] .control-wrap {
+ right: 10px;
+ text-align: center;
+}
+
+.carousel-wrap[data-full-width="false"] .control-wrap .carousel-prev {
+ padding-right: 10px;
+ padding-left: 10px;
+ margin-right: 0;
+}
+
+.carousel-wrap[data-full-width="false"] .control-wrap .carousel-next {
+ padding-left: 10px;
+ margin-left: 0;
+}
+
+.carousel-wrap[data-full-width="false"] .control-wrap .carousel-prev,
+.carousel-wrap[data-full-width="false"] .control-wrap .carousel-next {
+ right: 0 !important;
+ position: relative;
+ display: block;
+ float: left;
+}
+
+.ascend .carousel-next,
+.ascend .carousel-prev {
+ background-color: transparent;
+ height: 23px;
+ width: 10px;
+ line-height: 22px;
+ font-size: 14px;
+ cursor: pointer;
+ top: 1px !important;
+ transition: all .25s cubic-bezier(0.12, 0.75, 0.4, 1);
+ -webkit-transition: all .25s cubic-bezier(0.12, 0.75, 0.4, 1);
+ box-sizing: content-box;
+}
+
+.carousel-wrap .control-wrap .item-count {
+ height: 23px;
+ top: -1px;
+ right: -2px;
+ line-height: 22px;
+ letter-spacing: 4px;
+ position: relative;
+ display: block;
+ float: left;
+ z-index: 10;
+ cursor: pointer;
+ transition: all .25s cubic-bezier(0.12, 0.75, 0.4, 1);
+ -webkit-transition: all .25s cubic-bezier(0.12, 0.75, 0.4, 1);
+}
+
+.ascend [data-full-width="false"] .carousel-next:after,
+.ascend [data-full-width="false"] .carousel-prev:after {
+ display: block;
+ content: ' ';
+ position: absolute;
+ width: 24px;
+ height: 2px;
+ background-color: #000;
+ top: 8px;
+ opacity: 0;
+ left: -4px;
+ cursor: pointer;
+ transform: translateX(-20px);
+ transition: all .25s cubic-bezier(0.12, 0.75, 0.4, 1);
+ -webkit-transition: all .25s cubic-bezier(0.12, 0.75, 0.4, 1);
+}
+
+.ascend .light [data-full-width="false"] .carousel-next:after,
+.ascend .light [data-full-width="false"] .carousel-prev:after {
+ background-color: #fff;
+}
+
+.ascend .light .carousel-next i,
+.ascend .light .carousel-prev i {
+ color: #fff;
+}
+
+.ascend [data-full-width="false"] .carousel-next:after {
+ left: 5px;
+}
+
+.ascend [data-full-width="false"] .carousel-next:hover:after {
+ opacity: 1;
+ transform: translateX(-12px);
+}
+
+.ascend [data-full-width="false"] .carousel-prev:after {
+ transform: translateX(20px);
+}
+
+.ascend [data-full-width="false"] .carousel-prev:hover:after {
+ opacity: 1;
+ transform: translateX(0px);
+}
+
+.ascend [data-full-width="false"] .carousel-prev:hover i {
+ transform: translateX(-18px);
+}
+
+.ascend [data-full-width="false"] .carousel-prev.next-hovered,
+.ascend [data-full-width="false"] .item-count.next-hovered {
+ transform: translateX(-18px);
+}
+
+.ascend .carousel-next:hover,
+.ascend .carousel-prev:hover {
+ background-color: transparent !important;
+}
+
+.carousel-wrap[data-full-width="true"] .carousel-prev,
+.carousel-wrap[data-full-width="true"] .carousel-next {
+ border: none !important;
+}
+
+html .ascend .carousel-heading .container .carousel-prev {
+ right: 30px;
+}
+
+html .ascend[data-ext-responsive="true"] .carousel-heading .container .carousel-prev {
+ right: 40px;
+}
+
+.ascend .carousel-next i,
+.ascend .carousel-prev i {
+ color: #000;
+ font-size: 22px;
+ line-height: 17px;
+ height: 20px;
+ transition: all .25s cubic-bezier(0.12, 0.75, 0.4, 1);
+ -webkit-transition: all .25s cubic-bezier(0.12, 0.75, 0.4, 1);
+}
+
+.ascend .carousel-next:hover,
+.ascend .carousel-prev:hover {
+ border-color: transparent;
+}
+
+.ascend .light .carousel-next:hover i,
+.ascend .light .carousel-prev:hover i {
+ color: #fff;
+}
+
+.ascend .nectar-button.see-through,
+body.ascend .nectar-button.see-through,
+.swiper-slide .button a,
+body.ascend .nectar-button.see-through-2,
+.ascend .nectar-button,
+#to-top {
+ border-radius: 0 !important;
+ box-shadow: none !important;
+}
+
+.ascend #header-outer .cart-menu .cart-icon-wrap .icon-salient-cart {
+ font-size: 22px !important;
+ left: 0;
+ transition: all .2s linear !important;
+ -o-transition: all .2s linear !important;
+ -webkit-transition: all .2s linear !important;
+}
+
+.ascend[data-is="minimal"] #header-outer .cart-menu .cart-icon-wrap .icon-salient-cart {
+ font-size: 18px !important;
+}
+
+.ascend .icon-salient-cart:before {
+ content: "\e606";
+}
+
+.ascend #header-outer a.cart-contents .cart-wrap span:before {
+ display: none !important;
+}
+
+.ascend #header-outer .cart-menu .cart-icon-wrap {
+ width: 53px !important;
+}
+
+.ascend #header-outer .cart-menu {
+ padding-left: 29px;
+}
+
+.ascend #header-outer .cart-wrap {
+ top: -7px !important;
+ margin-right: 17px;
+}
+
+.ascend #header-outer .cart-menu-wrap {
+ right: 0 !important;
+}
+
+#header-outer .cart-wrap {
+ font-size: 11px;
+}
+
+.ascend #header-outer[data-full-width="true"] header#top nav ul #search-btn {
+ margin-left: 1px !important;
+ visibility: hidden;
+}
+
+.ascend #header-outer[data-full-width="true"][data-remove-border="true"] header#top nav ul #search-btn {
+ margin-left: 22px !important;
+}
+
+.ascend #header-outer[data-full-width="true"][data-cart="true"] header#top nav ul .slide-out-widget-area-toggle {
+ margin-left: 82px !important;
+ margin-right: -82px;
+ visibility: hidden;
+ position: relative;
+}
+
+.ascend[data-user-set-ocm="1"] #header-outer[data-full-width="true"][data-cart="false"] header#top nav ul .slide-out-widget-area-toggle {
+ visibility: hidden;
+ margin-right: -30px;
+ margin-left: 28px;
+}
+
+.ascend[data-header-search="false"] #header-outer[data-full-width="true"][data-cart="false"] header#top nav ul .slide-out-widget-area-toggle {
+ margin-left: 18px;
+}
+
+.ascend[data-header-search="false"] #header-outer[data-full-width="true"][data-cart="false"][data-format="centered-menu"] header#top nav ul #social-in-menu {
+ margin-right: 13px;
+}
+
+.ascend[data-header-search="false"] #header-outer[data-full-width="true"][data-cart="true"] header#top nav ul .slide-out-widget-area-toggle {
+ margin-left: 102px !important;
+}
+
+.ascend[data-slide-out-widget-area="true"] #header-outer[data-full-width="true"] .cart-menu-wrap {
+ right: 80px !important;
+}
+
+.ascend[data-slide-out-widget-area="true"] #header-outer[data-full-width="true"] .cart-outer[data-user-set-ocm="off"] .cart-menu-wrap {
+ right: 0 !important;
+}
+
+.ascend #header-outer[data-full-width="true"] header#top nav ul #search-btn a {
+ padding-left: 25px !important;
+ padding-right: 25px !important;
+}
+
+.ascend #header-outer[data-full-width="true"] header#top nav ul .slide-out-widget-area-toggle a {
+ padding-left: 28px !important;
+ padding-right: 28px !important;
+}
+
+.ascend #header-outer.transparent[data-transparent-header="true"][data-full-width="true"] header#top nav ul #search-btn a,
+.ascend #header-outer.transparent[data-transparent-header="true"][data-full-width="true"] header#top nav ul .slide-out-widget-area-toggle a {
+ border-left: 1px solid rgba(255, 255, 255, 0.25);
+}
+
+.ascend #header-outer[data-full-width="true"] header#top nav ul #search-btn a,
+.ascend #header-outer[data-full-width="true"] header#top nav ul .slide-out-widget-area-toggle a {
+ border-left: 1px solid rgba(0, 0, 0, 0.07);
+}
+
+.ascend[data-header-color="dark"] #header-outer[data-full-width="true"] header#top nav ul #search-btn a,
+body.ascend[data-header-color="dark"] #header-outer .cart-menu,
+.ascend[data-header-color="dark"] #header-outer[data-full-width="true"] header#top nav ul .slide-out-widget-area-toggle a {
+ border-left: 1px solid rgba(255, 255, 255, 0.13);
+}
+
+.ascend #header-outer[data-full-width="true"][data-cart="true"] header#top nav > ul.buttons,
+.ascend #header-outer[data-full-width="true"] header#top nav > ul.product_added.buttons,
+.ascend #boxed #header-outer[data-cart="true"] header#top nav > ul.buttons,
+.ascend #boxed #header-outer header#top nav > ul.product_added.buttons {
+ padding-right: 55px !important;
+}
+
+.ascend[data-header-search="false"][data-slide-out-widget-area="false"] #header-outer[data-full-width="true"][data-cart="true"] header#top nav > ul.buttons,
+.ascend[data-header-search="false"][data-slide-out-widget-area="false"] #header-outer[data-full-width="true"] header#top nav > ul.product_added.buttons,
+.ascend[data-header-search="false"][data-slide-out-widget-area="false"] #boxed #header-outer[data-cart="true"] header#top nav > ul.buttons,
+.ascend[data-header-search="false"][data-slide-out-widget-area="false"] #boxed #header-outer header#top nav > ul.product_added.buttons {
+ padding-right: 80px !important;
+}
+
+@media only screen and (min-width: 1000px) {
+.ascend #header-outer[data-full-width="true"][data-cart="false"] header > .container #search-btn {
+ margin-right: -28px !important;
+}
+}
+
+.ascend #header-outer[data-full-width="true"] header#top nav ul #search-btn > div {
+ border: none !important;
+}
+
+.ascend #header-outer a.cart-contents .cart-wrap span {
+ border-radius: 99px !important;
+ font: bold 11px/16px Arial;
+ line-height: 18px !important;
+ width: 18px !important;
+ padding: 0 1px !important;
+ visibility: hidden;
+}
+
+.ascend #header-outer .sf-menu ul li a {
+ border-bottom: none;
+}
+
+.ascend #header-outer .first-load a.cart-contents .cart-wrap span {
+ visibility: visible;
+ animation: .6s ease-in-out .12s normal both 1 bounce_in_animation;
+ -webkit-animation: .6s ease-in-out .12s normal both 1 bounce_in_animation;
+}
+
+.ascend #header-outer .static a.cart-contents span {
+ visibility: visible;
+}
+
+.ascend #header-outer .has_products .cart-menu .cart-icon-wrap .icon-salient-cart {
+ transition: all .2s linear !important;
+ -o-transition: all .2s linear !important;
+ -webkit-transition: all .2s linear !important;
+}
+
+body.ascend #header-outer .cart-menu {
+ border-left: 1px solid rgba(0, 0, 0, 0.07);
+ background-color: transparent !important;
+}
+
+body.ascend #boxed #header-outer .cart-menu-wrap {
+ position: absolute !important;
+ top: 0 !important;
+ box-shadow: none !important;
+}
+
+body.ascend #boxed #header-outer .widget_shopping_cart,
+body.ascend.woocommerce #boxed .cart-notification {
+ position: absolute !important;
+}
+
+body.ascend #boxed #header-outer .cart-menu-wrap .cart-menu {
+ box-shadow: none !important;
+}
+
+header#top #mobile-cart-link i {
+ line-height: 36px !important;
+}
+
+body[data-is="minimal"] header#top #mobile-cart-link i {
+ line-height: 34px !important;
+}
+
+body.ascend #search-outer {
+ background-color: rgba(255, 255, 255, 0.96) !important;
+ height: 95% !important;
+ position: fixed;
+ transform: rotateX(90deg);
+ padding: 0;
+ z-index: 1010 !important;
+}
+
+body.ascend #search-outer .container {
+ height: auto !important;
+ float: none !important;
+ width: 100% !important;
+ padding: 0 40px;
+ position: static;
+}
+
+#header-outer #search {
+ position: static !important;
+}
+
+body.ascend #search-outer #search input[type="text"] {
+ color: #000 !important;
+ height: auto !important;
+ font-size: 80px !important;
+ text-align: center !important;
+}
+
+#search-outer > #search form {
+ width: 100% !important;
+ float: none !important;
+}
+
+#search-outer > #search form,
+#search-outer #search .span_12 span {
+ opacity: 0;
+ position: relative;
+}
+
+#search-outer #search #close {
+ position: absolute;
+ top: 25px;
+ right: 25px;
+}
+
+#search-outer #search #close a {
+ right :0 !important;
+ top: 0 !important;
+ transition: all .47s cubic-bezier(0.3, 1, 0.3, 0.95) 0;
+ -webkit-transition: all .47s cubic-bezier(0.3, 1, 0.3, 0.95) 0;
+}
+
+#search-outer #search #close a:hover {
+ -ms-transform: rotate(90deg) translateZ(0);
+ -webkit-transform: rotate(90deg) translateZ(0);
+ transform: rotate(90deg) translateZ(0);
+}
+
+#search-outer #search .span_12 span {
+ text-align: center;
+ display: block;
+ color: rgba(0, 0, 0, 0.4);
+ margin-top: 15px;
+}
+
+#boxed #search-outer {
+ width: auto !important;
+ min-width: 1200px;
+ left: auto !important;
+}
+
+body.ascend #search-outer #search #close a span {
+ color: #000;
+}
+
+body.ascend #search-outer .ui-widget-content {
+ top: 90px !important;
+}
+
+.ascend #search-results .result span.bottom-line,
+.ascend .masonry-blog-item span.bottom-line,
+.ascend .masonry-blog-item .more-link {
+ display: none;
+}
+
+.ascend .masonry-blog-item .inner-wrap,
+.ascend .masonry-blog-item .mejs-container .mejs-controls {
+ box-shadow: none !important;
+}
+
+.ascend .masonry-blog-item .post-meta {
+ padding: 0 18px 18px !important;
+}
+
+.ascend .post .nectar-love-wrap {
+ line-height: 20px;
+}
+
+.ascend .masonry-blog-item .inner-wrap {
+ padding: 0;
+}
+
+.ascend .masonry-blog-item .content-inner .post-featured-img img,
+.ascend .masonry-blog-item .more-link,
+#post-area.masonry article.post .quote-inner,
+#post-area.masonry article.post .link-inner,
+#post-area.masonry article.post .status-inner,
+#post-area.masonry article.post .aside-inner {
+ margin-bottom: 0 !important;
+}
+
+.ascend .masonry-blog-item .content-inner {
+ padding-bottom: 0 !important;
+ border: 0 !important;
+ margin-bottom: 0 !important;
+}
+
+.ascend .masonry-blog-item .article-content-wrap {
+ padding: 16px 18px 18px !important;
+}
+
+.ascend #post-area.masonry article.post.quote .post-content .post-meta,
+.ascend #post-area.masonry article.post.link .post-content .post-meta,
+.ascend #post-area.masonry article.format-status .post-content .post-meta,
+.ascend #post-area.masonry article.post.format-aside .post-meta {
+ display: none !important;
+}
+
+.ascend article.post .content-inner {
+ border: none !important;
+}
+
+.ascend #author-bio {
+ padding-bottom: 20px;
+ border: none;
+ text-align: center;
+}
+
+.ascend #author-bio img {
+ display: block;
+ margin: 0 auto;
+ margin-bottom: 15px !important;
+ position: relative;
+}
+
+.ascend #author-bio h3 span,
+.ascend .comments-section .comment-wrap.full-width-section > h3 span {
+ display: block;
+ margin-bottom: 5px;
+ line-height: 12px;
+ font-size: 12px;
+ text-transform: none;
+}
+
+.ascend .container-wrap #author-bio #author-info {
+ width: 600px !important;
+ margin: 0 auto;
+ padding-left: 0 !important;
+}
+
+.ascend .container-wrap #author-bio #author-info p {
+ max-width:70%;
+ margin:0 auto;
+}
+
+.ascend #author-bio .nectar-button {
+ margin-top: 20px;
+}
+
+.ascend #author-bio .avatar {
+ border-radius: 100%;
+}
+
+.ascend .comment-list {
+ margin-bottom: 0 !important;
+}
+
+.ascend .comment-list .reply {
+ top: 7px;
+}
+
+.ascend .comment-list .reply a {
+ color: #000;
+ background-color: transparent;
+ border-radius: 0 !important;
+}
+
+html .ascend .comment-list .reply a:hover {
+ color: #fff !important;
+}
+
+.ascend #respond {
+ margin-top: 80px !important;
+}
+
+.ascend h3#comments {
+ text-align: center;
+}
+
+.comment-list li.comment > div,
+.comment-list li.pingback > div {
+ background-color: transparent !important;
+ box-shadow: none !important;
+ padding-left: 85px !important;
+ padding-bottom: 0 !important;
+ padding-top: 0 !important;
+ margin-top: 4em !important;
+}
+
+.comment-list li.comment > div img.avatar,
+.comment-list li.pingback > div img.avatar {
+ left: 0 !important;
+ border-radius: 100%;
+ top: 0 !important;
+}
+
+.comment-list li.comment > div p,
+.comment-list li.pingback > div p {
+ margin-top: 30px;
+}
+
+.comment-list .children {
+ background-position:left 30px !important;
+ margin-left: 26px !important;
+ padding-left: 40px !important;
+}
+
+.comment-list .says {
+ display: none;
+}
+
+.ascend #reply-title {
+ margin-bottom: 50px;
+ text-align: center !important;
+}
+
+.ascend.single-product #reply-title,
+.ascend.single-product #commentform .form-submit {
+ text-align: left !important;
+}
+
+.ascend .comment #reply-title {
+ text-align: left !important;
+}
+
+.ascend #respond #cancel-comment-reply-link {
+ padding-left: 15px;
+}
+
+.ascend .comment-wrap {
+ padding-top: 0 !important;
+ margin-top: 0 !important;
+}
+
+.ascend.single-portfolio .comment-wrap {
+ margin-top: 30px !important;
+}
+
+.ascend .comment-wrap h3#comments {
+ padding-top: 80px;
+}
+
+.ascend #author-bio.no-pagination,
+.ascend .comment-wrap {
+ border-top: 1px solid #999;
+}
+
+.ascend .comments-section[data-author-bio="false"] .comment-wrap {
+ border: none;
+}
+
+.ascend .comment-list {
+ padding-bottom: 80px;
+ border-bottom: 1px solid #999;
+}
+
+.ascend #author-bio.no-pagination.lighter-grey,
+.ascend .comment-wrap.lighter-grey {
+ border-color: #ddd;
+}
+
+.ascend .comment-wrap,
+.ascend #author-bio {
+ padding-top: 80px;
+}
+
+.single-post.ascend #page-header-bg.fullscreen-header,
+.single-post #single-below-header.fullscreen-header {
+ background-color: #f6f6f6;
+}
+
+.single-post.ascend #single-below-header.fullscreen-header {
+ border-top: 1px solid #DDD;
+ border-bottom: none !important;
+}
+
+.tagcloud a,
+#header-outer .widget_shopping_cart a.button,
+article.post .more-link span,
+.blog-recent .more-link span {
+ border-radius: 0 !important;
+}
+
+.flex-direction-nav a,
+#pagination span,
+#pagination a,
+#pagination .next.inactive,
+#pagination .prev.inactive,
+.woocommerce nav.woocommerce-pagination ul li a,
+.woocommerce .container-wrap nav.woocommerce-pagination ul li span {
+ border-radius: 0 !important;
+}
+
+.ascend .col.boxed,
+.ascend .wpb_column.boxed {
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+ -o-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+ background-color: #fff;
+ padding: 25px 15px 15px;
+ position: relative;
+ -webkit-transition: transform .2s linear,-webkit-box-shadow .2s linear;
+ -moz-transition: transform .2s linear,-moz-box-shadow .2s linear;
+ transition: transform .2s linear,box-shadow .2s linear;
+ top: 0 !important;
+}
+
+.ascend .col.boxed:hover,
+.ascend .wpb_column.boxed:hover {
+ -ms-transform: translateY(-4px) !important;
+ -webkit-transform: translateY(-4px) !important;
+ transform: translateY(-4px) !important;
+ -webkit-box-shadow: 0 17px 25px rgba(0, 0, 0, 0.13);
+ -moz-box-shadow: 0 17px 25px rgba(0, 0, 0, 0.13);
+ -o-box-shadow: 0 17px 25px rgba(0, 0, 0, 0.13);
+ box-shadow: 0 17px 25px rgba(0, 0, 0, 0.13) !important;
+}
+
+.ascend a.pp_arrow_previous,
+.ascend a.pp_arrow_next,
+.ascend .mfp-arrow-right,
+.ascend .mfp-arrow-left,
+.woocommerce .pswp__button--arrow--right,
+.woocommerce .pswp__button--arrow--left {
+ overflow: visible;
+ height: 40px;
+ width: 40px;
+ border-radius: 100px;
+ border: 2px solid rgba(255, 255, 255, 0.4);
+}
+
+.woocommerce .pswp__button--arrow--right:before,
+.woocommerce .pswp__button--arrow--left:before {
+ background-image: none;
+ line-height: 40px !important;
+ height: 40px !important;
+ width: 36px !important;
+}
+
+a.pp_arrow_previous {
+ left: 30px;
+}
+
+a.pp_arrow_next {
+ right: 30px;
+}
+
+.ascend a.pp_arrow_next:hover,
+.ascend a.pp_arrow_previous:hover,
+.ascend .mfp-arrow-right:hover,
+.ascend .mfp-arrow-left:hover,
+.woocommerce .pswp__button--arrow--right:hover,
+.woocommerce .pswp__button--arrow--left:hover {
+ border: 2px solid rgba(255, 255, 255, 0);
+}
+
+.ascend a.pp_arrow_previous .icon-default-style {
+ border-radius:0 !important;
+ width: 20px !important;
+ height: 40px !important;
+ line-height: 39px !important;
+ font-size: 24px !important;
+ font-family: FontAwesome !important;
+ margin-top: 0 !important;
+ left: -1px;
+ transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+ -webkit-transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+}
+
+.ascend .mfp-arrow-left,
+.woocommerce .pswp__button--arrow--left {
+ height: 40px !important;
+ line-height: 40px !important;
+ font-size: 24px !important;
+ font-family: FontAwesome !important;
+ margin-top: 0 !important;
+ opacity: 1;
+ left: 40px;
+ transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+ -webkit-transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+}
+
+.ascend a.pp_arrow_next .icon-default-style {
+ border-radius: 0 !important;
+ width: 20px !important;
+ line-height: 39px !important;
+ height: 40px !important;
+ font-size: 24px !important;
+ margin-top: 0 !important;
+ font-family: FontAwesome !important;
+ left: 1px;
+ transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+ -webkit-transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+}
+
+.ascend .mfp-arrow-right,
+.woocommerce .pswp__button--arrow--right {
+ line-height: 40px !important;
+ height: 40px !important;
+ font-size: 24px !important;
+ margin-top: 0 !important;
+ font-family: FontAwesome !important;
+ opacity: 1;
+ right: 40px;
+ transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+ -webkit-transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+}
+
+.ascend a.pp_arrow_previous .icon-default-style:after,
+.ascend a.pp_arrow_next .icon-default-style:after,
+.ascend .mfp-arrow-left:after,
+.ascend .mfp-arrow-right:after,
+.woocommerce .pswp__button--arrow--right:after,
+.woocommerce .pswp__button--arrow--left:after {
+ display: block;
+ content: ' ';
+ position: absolute;
+ width: 36px;
+ height: 2px;
+ background-color: #fff;
+ top: 19px;
+ opacity: 0;
+ left: -6px;
+ cursor: pointer;
+ transform: translateX(-37px);
+ transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+ -webkit-transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+ pointer-events: none;
+}
+
+.ascend .mfp-arrow-left:after,
+.ascend .mfp-arrow-right:after,
+.woocommerce .pswp__button--arrow--right:after,
+.woocommerce .pswp__button--arrow--left:after {
+ top: 17px;
+ -ms-transform: translateX(-27px);
+ -webkit-transform: translateX(-27px);
+ transform: translateX(-27px);
+}
+
+.ascend .mfp-arrow-left:after,
+.woocommerce .pswp__button--arrow--left:after {
+ left: 65px;
+}
+
+.ascend a.pp_arrow_previous .icon-default-style:after {
+ left: 59px;
+}
+
+.ascend a.pp_arrow_previous .icon-default-style:before,
+.mfp-arrow-left:before,
+.woocommerce .pswp__button--arrow--left:before {
+ content: "\f104";
+ color: #fff;
+}
+
+.mfp-arrow-left:before,
+.woocommerce .pswp__button--arrow--left:before {
+ top: -3px;
+ left: -1px;
+ display: block;
+ position: relative;
+ transition: all .35s cubic-bezier(0.12, 0.75 , 0.4, 1);
+ -webkit-transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+}
+
+.ascend a.pp_arrow_next .icon-default-style:before,
+.mfp-arrow-right:before,
+.woocommerce .pswp__button--arrow--right:before {
+ content: "\f105";
+ color: #fff;
+}
+
+.mfp-arrow-right:before,
+.woocommerce .pswp__button--arrow--right:before {
+ top: -3px;
+ right: -1px;
+ display: block;
+ transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+ -webkit-transition: all .35s cubic-bezier(0.12, 0.75, 0.4, 1);
+ position: relative;
+}
+
+.ascend a.pp_arrow_next:hover .icon-default-style:after,
+.mfp-arrow-right:hover:after,
+.woocommerce .pswp__button--arrow--right:hover:after {
+ opacity: 1;
+ -ms-transform: translateX(-18px);
+ -webkit-transform: translateX(-18px);
+ transform: translateX(-18px);
+}
+
+.mfp-arrow-right:hover:before,
+.woocommerce .pswp__button--arrow--right:hover:before {
+ -ms-transform: translateX(9px);
+ -webkit-transform: translateX(9px);
+ transform: translateX(9px);
+}
+
+.mfp-arrow-left:hover:before,
+.woocommerce .pswp__button--arrow--left:hover:before {
+ -ms-transform: translateX(-9px);
+ -webkit-transform: translateX(-9px);
+ transform: translateX(-9px);
+}
+
+.mfp-arrow-right:hover:after,
+.woocommerce .pswp__button--arrow--right:hover:after {
+ -ms-transform: translateX(-1px);
+ -webkit-transform: translateX(-1px);
+ transform: translateX(-1px);
+}
+
+.ascend a.pp_arrow_next:hover .icon-default-style {
+ -ms-transform: translateX(7px);
+ -webkit-transform: translateX(7px);
+ transform: translateX(7px);
+}
+
+.ascend a.pp_arrow_previous:hover .icon-default-style:after,
+.mfp-arrow-left:hover:after,
+.woocommerce .pswp__button--arrow--left:hover:after {
+ opacity: 1;
+ -ms-transform: translateX(-51px);
+ -webkit-transform: translateX(-51px);
+ transform: translateX(-51px);
+}
+
+.mfp-arrow-left:hover:after,
+.woocommerce .pswp__button--arrow--left:hover:after {
+ -ms-transform: translateX(-58px);
+ -webkit-transform: translateX(-58px);
+ transform: translateX(-58px);
+}
+
+.ascend a.pp_arrow_previous:hover .icon-default-style {
+ -ms-transform: translateX(-7px);
+ -webkit-transform: translateX(-7px);
+ transform: translateX(-7px);
+}
+
+.container-wrap input[type="text"],
+.container-wrap textarea,
+.container-wrap input[type="email"],
+.container-wrap input[type="password"],
+.container-wrap input[type="tel"],
+.container-wrap input[type="url"],
+.container-wrap input[type="search"],
+.container-wrap input[type="date"] {
+ background-color: transparent !important;
+ border: 1px solid #ccc !important;
+ -webkit-box-shadow: none !important;
+ -o-box-shadow: none !important;
+ box-shadow: none !important;
+ font-size: 16px !important;
+ padding: 16px !important;
+}
+
+.container-wrap input[type="text"]:focus,
+.container-wrap textarea:focus,
+.container-wrap input[type="email"]:focus,
+.container-wrap input[type="password"]:focus,
+.container-wrap input[type="tel"]:focus,
+.container-wrap input[type="url"]:focus,
+.container-wrap input[type="search"]:focus,
+.container-wrap input[type="date"]:focus {
+ border-color: #999 !important;
+}
+
+.ascend #commentform .form-submit {
+ text-align: center;
+ padding-bottom: 50px;
+ margin-top: 20px;
+}
+
+.ascend .container-wrap input[type="submit"],
+.ascend .container-wrap button[type="submit"],
+.woocommerce-cart .wc-proceed-to-checkout a.checkout-button {
+ padding: 16px !important;
+ border-radius: 0 !important;
+}
+
+body[data-button-style="rounded"].ascend .container-wrap input[type="submit"],
+body[data-button-style="rounded"].ascend .container-wrap button[type="submit"] {
+ padding: 16px 23px !important;
+}
+
+body[data-button-style="rounded"][data-form-submit="see-through"].ascend .container-wrap .widget_search input[type="submit"],
+body[data-button-style="rounded"][data-form-submit="see-through"].ascend .container-wrap .widget_search button[type="submit"] {
+ padding: 12px 23px !important;
+}
+
+.ascend .woocommerce .actions .button {
+ height: auto !important;
+ padding: 14px !important;
+}
+
+.ascend .cart .quantity input.plus,
+.ascend .cart .quantity input.minus {
+ font-weight: 400 !important;
+ height: 46px;
+ font-size: 16px;
+ width: 46px;
+}
+
+.ascend .cart .quantity input.qty {
+ height: 46px;
+ width: 46px;
+}
+
+.ascend .widget_search .search-form input[type=submit],
+.ascend .newsletter-widget form input[type=submit] {
+ line-height: 24px;
+}
+
+.container-wrap .span_12.light input[type="text"],
+.container-wrap .span_12.light textarea,
+.container-wrap .span_12.light input[type="email"],
+.container-wrap .span_12.light input[type="password"],
+.container-wrap .span_12.light input[type="tel"],
+.container-wrap .span_12.light input[type="url"],
+.container-wrap .span_12.light input[type="search"],
+.container-wrap .span_12.light input[type="date"] {
+ border: 1px solid rgba(255, 255, 255, 0.6) !important;
+ color: #fff;
+}
+
+.container-wrap .span_12.light input[type="text"]:focus,
+.container-wrap .span_12.light textarea:focus,
+.container-wrap .span_12.light input[type="email"]:focus,
+.container-wrap .span_12.light input[type="password"]:focus,
+.container-wrap .span_12.light input[type="tel"]:focus,
+.container-wrap .span_12.light input[type="url"]:focus,
+.container-wrap .span_12.light input[type="search"]:focus,
+.container-wrap .span_12.light input[type="date"]:focus {
+ border: 1px solid rgba(255, 255, 255, 0.8) !important;
+}
+
+.container-wrap .span_12.light input[type="submit"]:hover {
+ background-color: #333 !important;
+ opacity: .8 !important;
+}
+
+/* RTL */
+.rtl .comment-list li.comment > div,
+.rtl .comment-list li.pingback > div {
+ padding-right: 85px !important;
+ padding-left: 25px !important;
+}
+
+.rtl .comment-list li.comment > div img.avatar,
+.rtl .comment-list li.pingback > div img.avatar {
+ right: 0 !important;
+ left: auto !important;
+}
+
+body.rtl .carousel-wrap[data-full-width="false"] .control-wrap {
+ left: 0px;
+ right: auto;
+}
+
+.rtl .carousel-wrap[data-full-width="false"] .control-wrap .carousel-prev {
+ margin-left: 0;
+}
+
+.rtl .carousel-wrap[data-full-width="false"] .control-wrap .carousel-prev,
+.carousel-wrap[data-full-width="false"] .control-wrap .carousel-next {
+ left: 0 !important;
+ right: auto !important;
+}
+
+.rtl .carousel-wrap[data-full-width="false"] .control-wrap .item-count {
+ left: -2px;
+ right: auto;
+}
+
+.rtl.ascend [data-full-width="false"] .carousel-prev.next-hovered,
+.rtl.ascend [data-full-width="false"] .item-count.next-hovered {
+ transform: translateX(0px);
+}
+
+.rtl.ascend [data-full-width="false"] .carousel-next:hover {
+ transform: translateX(18px);
+}
+
+.rtl.ascend [data-full-width="false"] .carousel-prev:hover ~ .carousel-next ,
+.rtl.ascend [data-full-width="false"] .carousel-prev:hover ~ .item-count {
+ transform: translateX(18px);
+}
+
+.rtl.ascend [data-full-width="false"] .carousel-prev:hover i {
+ transform: translateX(0px);
+}
+
+.rtl.ascend [data-full-width="false"] .carousel-prev:after {
+ transform: translateX(30px);
+}
+
+.rtl.ascend [data-full-width="false"] .carousel-prev:hover:after {
+ opacity: 1;
+ transform: translateX(18px);
+}
+
+body a {
+ color: #1080A7;
+}
diff --git a/3rd_party/static/onap-ui/assets/css/combine.css b/3rd_party/static/onap-ui/assets/css/combine.css new file mode 100644 index 0000000..ce80767 --- /dev/null +++ b/3rd_party/static/onap-ui/assets/css/combine.css @@ -0,0 +1,4033 @@ +html { + overflow-y: scroll; +} + +hr { + height: 1px !important; +} + +h3 { + margin-top: 10px; + margin-bottom: 10px; +} + +a, +a:visited { + color: #bc1518; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +.center { + text-align: center; +} + +@media (max-width: 950px) { + .defaultSkin table.mceLayout { + width: 100% !important; + } +} + +/* fix for input-group bootstrap */ +.input-group .input-group-btn .btn { + height: 34px; +} + +/* @group Navigation */ +#navigation { + display: block; + background: url('/themes/openstack/images/header-line.gif') repeat-x 0 bottom; + padding-bottom: 1px; +} + +#navigation * { + padding: 0; + margin: 0; +} + +#navigation ul { + display: block; + margin: 0 auto; +} + +#navigation li { + display: block; + float: left; + margin-right: 20px; +} + +#navigation li a { + display: block; + font-weight: normal; + text-decoration: none; + background-position: 50% 0; + padding: 20px 0 5px; + color: #353535; + font-size: 14px; +} + +#navigation li a.current, +#navigation li a.section { + border-bottom: 3px solid #cf2f19; + color: #cf2f19; +} + +/* @group Auto-clearing */ +#navigation:after, +#navigation ul:after, +#header:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +#navigation, +#navigation ul, +#header:after { + display: inline-block; +} + +/* Hides from IE-mac \*/ +* html #navigation, +* html #navigation ul { + height: 1%; +} + +#navigation, +#navigation ul, +#header:after { + display: block; +} + +/* End hide from IE-mac */ +/* @end */ +#logo a { + display: block; + margin-top: 8px; + text-indent: -1000em; + background: url('/themes/openstack/images/open-stack-cloud-computing-logo-2.png') no-repeat left center; + height: 54px; + width: 177px; + margin-left: -10px; +} + +#header { + margin-bottom: 0px; + margin-top: 20px; +} + +/* @end */ +/* @group Tabs */ +.tabContentHidden { + display: none; +} + +.tabTops { + border: 1px solid black; + border-bottom: none; +} + +#subnav li { + list-style-type: none; + margin-bottom: 5px; + padding: 0; +} + +#subnav ul { + padding-left: 0px; + margin-right: 0px; +} + +#subnav ul.overviewNav li a { + display: block; + padding: 10px 20px 10px 42px; + text-decoration: none; + color: black; + background-color: #e9e9e9; + background-image: none; + text-align: right; + margin-right: 40px; +} + +#subnav ul.overviewNav li a:hover { + background-color: #d6d6d6; + -webkit-transition: background-color 1s ease-out; +} + +ul.subsectionNav li.current a { + background-color: #c4e0e9; + -webkit-transition: background-color 1s ease-out; +} + +.subsectionNav a[href*="/essex/"] { + background-color: #E9E9E9 !important; +} + +.subsectionNav a[href*="/start/"] { + background-color: #D5EFD4 !important; + margin-top: 30px; +} + +.subsectionNav a[href*="/marketplace/training/"] { + margin-top: 30px; +} + +.overviewNav li.active, +.subsectionNav li.active { + background: url('/themes/openstack/images/pointer-arrow.gif') no-repeat right center; +} + +#subnav ul li { + text-align: right; +} + +ul.subsectionNav li a { + display: block; + padding: 10px 20px 10px 42px; + text-decoration: none; + color: black; + background: #e2ecef none no-repeat 5px center; + margin-right: 40px; +} + +ul.subsectionNav li a:hover { + background-color: #c4e0e9; + -webkit-transition: background-color 1s ease-out; +} + +ul.tabs { + padding: 0 2px 0 0; + white-space: nowrap; + list-style-type: none; + display: block; + zoom: 1; + margin-right: 0px; + clear: both; + border-bottom: 1px solid #d8d8d8; + background-color: #ececec; +} + +.tabSet { + margin: auto; + background-color: #f5f5f5; + -webkit-border-radius: 3px; + border-radius: 3px; + border: 1px solid #d8d8d8; + border-top: 1px solid #bcbcbc; +} + +#home .tabSet { + margin-top: 45px; +} + +.featureHeader { + margin-left: 20px; + margin-top: 20px; +} + +.tabs li { + cursor: pointer; + display: inline; + margin-right: -3px; + padding: 0px; +} + +.tabs li a { + margin: 0px; + display: inline-block; + color: #41728d; + font-size: 13px; + font-family: 'PT Sans', serif; + padding: 6px 14px; + text-shadow: #fff 0px 1px 1px; + border-right: 1px solid #d8d8d8; + width: 160px; + text-align: center; +} + +.tabs li a:hover { + background-color: #eee; + text-decoration: none; +} + +.tabs li.active a { + cursor: default; + text-decoration: none; + position: relative; + color: black; + background-color: #f5f5f5; + border-bottom: 1px solid #f5f5f5; + margin-bottom: -1px; + border-top: 1px white solid; + font-weight: bold; +} + +.tabs li#showcode a { + background: #6b90da; + padding-bottom: 6px; + font-weight: bold; + color: #fff; +} + +/* @end */ +/* @group Buttons */ +a.button { + font-family: 'PT Sans', serif; + border: 1px solid #ccc; + padding: 3px 30px; + color: #525252; + text-decoration: none; + font-size: 14px; + line-height: 3em; + -webkit-box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); + -moz-box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); + box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); + text-shadow: #fff 0px 1px 1px; + background: -webkit-gradient(linear, left top, left bottom, from(#eeeeee), to(#bebebe)); + background: #ddd; +} + +a.button:hover { + color: black; + -webkit-transition: color 1s ease-out; +} + +a.button:active { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + border-color: #ababab; + border-top-color: #636363; + background: #ababab; + background: -webkit-gradient(linear, left top, left bottom, from(#bebebe), to(#dddddd)); + -webkit-transition: none; + padding: 4px 29px 2px 31px !important; +} + +/* @end */ +/* @group Rounded Buttons */ +.roundedButton, +input.action { + font-family: 'PT Sans', serif; + border: 1px solid #e2e2e2; + padding: 4px 15px; + color: black !important; + text-decoration: none !important; + font-size: 12.5px; + line-height: 3em; + background: #FFFFFF; + /* old browsers */ + /* firefox */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #FFFFFF), color-stop(50%, #F3F3F3), color-stop(100%, #EBEBEB)); + /* webkit */ + -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); + box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.5); + text-shadow: #fff 0px 1px 1px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + border-bottom-color: #a0a0a0; + border-right-color: #bababa; + border-left-color: #bababa; +} + +.roundedButton:hover, +input.action:hover { + color: black; + -webkit-transition: color 1s ease-out; + cursor: pointer; +} + +a.roundedButton:active, +input.action:active { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + border-color: #ababab; + border-top-color: #636363; + background: #ababab; + background: -webkit-gradient(linear, left top, left bottom, from(#bebebe), to(#dddddd)); + -webkit-transition: none; + padding: 4px 15px; +} + +input.action { + line-height: 1.2em !important; +} + +/* @end */ +.subhead { + color: #cf2f19; + font-size: 16px; + border-bottom: 1px dotted; + padding-bottom: 5px; + border-color: #c5e2ea; + margin-bottom: 20px; +} + +/* @group projects page */ +.projectsPage h1, +.communityPage h1, +#blog h1 { + color: #264d69; + font-size: 24px; +} + +.projectsPage h2 { + color: #264d69; + font-size: 17px; +} + +#subnav { + padding-top: 38px; +} + +.note { + color: #33730a; + background: #e2f4dc url('/themes/openstack/images/side-note-pointer.gif') no-repeat left center; +} + +.note a { + color: #33730a; + text-decoration: underline; +} + +.note p { + margin-left: 8px; + padding: 10px; + margin-bottom: 0px; + border-bottom: 1px solid #b5c8a8; +} + +a#CitrixVideo { + display: block; + padding-top: 93px; + background: url('/themes/openstack/images/citrix-video-thumbnail.jpg') no-repeat; + color: #aeaeae; + text-decoration: none; + margin-top: 3px; +} + +h3.videoHeader { + color: #939393; + font-size: 14px; +} + +a.downloadLink { + text-decoration: none; + color: white; + font-family: helvetica, arial; + font-weight: bold; + display: block; + width: 250px; + text-align: center; + position: relative; + padding: 3px; + margin-bottom: 5px; + margin-top: 5px; + /* BORDER RADIUS */ + border-radius: 5px; + background-color: #989996; + border: 2px solid white; +} + +/* @end */ +#footer { + margin-top: 30px; +} + +/* @group compute */ +.projectVitals { + border-top: 1px solid #c5e2ea; + padding-top: 10px; +} + +.projectVitals h3 { + font-size: 16px; + color: #264d69; +} + +#status { + padding: 12px; + color: #1d6006; + background-color: #e2f4dc; + border: 1px solid #b5c8a8; + margin-bottom: 10px; + height: 9em; +} + +#status strong { + font-size: 120%; +} + +#availability { + line-height: 1.4em; + padding: 12px; + color: #747474; + background-color: #f6f8f8; + border: 1px solid #d4d5d5; + margin-bottom: 10px; + height: 9em; +} + +.projectVitals h4 { + font-size: 14px; + color: #797979; + margin-bottom: 4px; +} + +#status p, +#availability p { + margin: 0px; +} + +#parallax { + background: #2f3134; + position: relative; + overflow: hidden; + width: 60em; + height: 300px; + margin: 1.5em 0; +} + +/* @group FAQ */ +.faqs .span-5 { + font-size: 14px; + color: #707070; + font-weight: bold; +} + +.faqs hr { + padding: 0px; +} + +.faqs div { + margin-bottom: 20px; +} + +/* @end */ +/* @end */ +/* @group community page */ +.communityBox { + height: 213px; + background: #f8f8f8 url('/themes/openstack/images/community-box-headers.png') no-repeat 0 0; +} + +#userResources { + background-position: -200px 0; +} + +#devCenter { + background-position: -400px 0; +} + +.communityBox p, +.communityBox h2 { + margin: 15px; +} + +.communityBox, +.communityBox a, +.communityBox a.visited { + color: #6b6b6b; +} + +.communityBox a { + text-decoration: underline; +} + +.communityBox h2 { + color: black; + font-size: 17px; + margin-top: 60px; +} + +.participants h2, +.communityResources h2 { + font-size: 16px; + color: #264d69; + margin-top: 30px; + padding-top: 10px; + border-top: 1px dotted #c5e2ea; +} + +#designSummit h2 { + text-indent: -1000px; + height: 222px; + margin-top: -9px; + margin-bottom: 10px; + background: url('/themes/openstack/images/openstack-design-summit-community.jpg') no-repeat 0 0; +} + +#designSummit { + color: #6b6b6b; +} + +#designSummit strong { + color: black; + font-weight: normal; +} + +/* @end */ +/* @group blog */ +#blog h2 { + color: #5189a0; + font-size: 15px; + margin-bottom: 0px; +} + +#blog h2 a { + background-color: #eaeaea; + text-align: center; + padding: 1px; + padding-left: 7px; + padding-right: 7px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + color: #757575; + font-size: 0.7em; + text-decoration: none; +} + +div.byline { + border-bottom: 1px dotted #c5e2ea; + border-top: 1px dotted #c5e2ea; + margin-bottom: 10px; + padding: 2px 0; +} + +div.byline p { + margin: 0px; +} + +div.byline .postDate { + text-align: right; +} + +div.byline p.name a, +div.byline p.name a:visited { + color: #cf2f19; + text-decoration: none; +} + +div.socialMedia { + background-color: #eef3f5; + margin-bottom: 20px; +} + +div.socialMedia div { + padding: 10px; +} + +div.socialMedia h4 { + margin-bottom: 2px; +} + +div.socialMedia p { + margin-bottom: 0px; +} + +div.socialMedia div.twitter { + border-bottom: 1px dotted #c5e2ea; +} + +#blog h3 { + margin-bottom: 5px; + font-size: 12px; +} + +/* @end */ +/* @group Brand */ +div.termsBox { + border: 1px solid #c6e2ea; + padding: 10px; + height: 300px; + overflow: scroll; + margin-bottom: 10px; +} + +.termsBox h3, +#openstack-trademark-policy h3 { + font-size: 100%; + font-weight: bold; +} + +.termsBox a { + color: inherit; + text-decoration: underline; +} + +#zenbox_tab { + top: 34% !important; + min-width: 109px !important; + line-height: 0px !important; +} + +/* @end */ +/* @group quotes */ +ul#quotes { + margin-left: 0px; + padding-left: 0px; + margin-top: 10px; +} + +ul#quotes li { + list-style-type: none; +} + +ul#quotes li p { + line-height: 1.4em; +} + +ul#quotes p { + font-size: 16px; + font-family: 'PT Sans', serif; + margin-bottom: 0px; + color: black; + line-height: 1.2em; + padding: 10px; + padding-bottom: 0px; +} + +ul#quotes p.name { + margin-top: 10px; + font-size: 14px; + text-transform: uppercase; + color: #9b9b9b; + text-indent: 0px; +} + +ul#quotes p.name strong { + font-weight: normal; + color: #494949; +} + +/* @end */ +.tooltip { + background: black; + background: rgba(0, 0, 0, 0.8); + padding: 1px 8px; + color: white; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} + +a.Datasheet { + padding-top: 110px; + display: block; + background: url('/themes/openstack/images/openstack-product-pdf.jpg') no-repeat center top; + margin-left: 20px; + margin-top: 30px; +} + +a.Datasheet:hover { + text-decoration: none; + color: #bc1518; +} + +a#DemoVideo { + display: block; + padding-top: 103px; + margin-left: 18px; + background: url('/themes/openstack/images/demo-video-thumbnail.jpg') no-repeat; + text-decoration: none; + margin-top: 50px; +} + +/* @group Tables */ +.tabContent table { + margin: 20px; + width: 670px; +} + +.tabContent table td { + border-bottom: 1px solid #d8d8d8; + vertical-align: top; + padding: 10px 10px 20px 0; +} + +.tabContent table td p { + margin: 0px; +} + +.tabContent table tr:last-child td { + border-bottom: none; +} + +.tabContent table th { + font-family: 'PT Sans', serif; + font-style: normal; + font-weight: normal; + font-size: 18px; + letter-spacing: -0.076em; + line-height: 1em; + color: #264d69; + padding-left: 0px; +} + +.tabContent table a:active, +.tabContent table a:visited, +.tabContent table a { + color: inherit; + text-decoration: underline; +} + +p.fnote { + margin-left: 20px; +} + +/* @end */ +h2.user-story-quote { + line-height: 1.5em; + color: grey !important; +} + +.user-story-quote-author { + text-transform: uppercase; +} + +ul.user-project-list { + margin: 0px; + padding: 0px; + overflow: hidden; + margin-bottom: 20px; +} + +ul.user-project-list li { + display: block; + margin-right: 5px; + background-color: #e2f1f5; + border: 1px solid #89c6d6; + -webkit-border-radius: 3px; + border-radius: 3px; + padding: 2px 8px; + margin-bottom: 5px; + color: #1a4b6b; + font-size: 95%; + width: 170px; + float: left; +} + +#footer ul { + margin: 10px 0px 20px; + padding: 0px; + list-style: none; +} + +#footer a, +#footer a:visited, +#footer a:active { + color: black; +} + +#footer h3 { + color: #de0000; + font: 130% 'PT Sans', serif; +} + +#footer textarea { + width: 260px; + height: 60px; + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #EBEBEB), color-stop(5%, #FFFFFF)); + /* webkit */ +} + +#footer form { + margin-top: 8px; + margin-bottom: 20px; +} + +.user-links li { + padding-left: 20px; + background: url('/themes/openstack/images/link.png') no-repeat left center; + margin-left: -20px; + list-style: none; +} + +.user-objectives { + background-color: #ecedec; + padding: 20px; + margin-bottom: 15px; +} + +.user-objectives p { + margin-bottom: 0px; +} + +.user-name { + padding-top: 10px; +} + +.user-photo img { + border: 5px solid white; + -webkit-box-shadow: 3px 2px 2px rgba(0, 0, 0, 0.3); + box-shadow: 3px 2px 2px rgba(0, 0, 0, 0.3); +} + +.siteMessage { + -webkit-border-radius: 4px; + border-radius: 4px; + border: 1px solid; + margin-bottom: 10px; + margin-top: 20px; +} + +.siteMessage p { + margin: 8px; +} + +#InfoMessage { + color: black; + background-color: #DBEAEE; + border-color: #B5D8E2; + font-size: 120%; + padding: 10px; +} + +#SuccessMessage { + color: #3E933A; + background-color: #E2F7D8; + border-color: #9FDE9C; +} + +#ErrorMessage { + color: #DA1D1D; + background-color: #FFDFDF; + border-color: #FFBBBB; +} + +p.message.bad { + color: #DA1D1D; + background-color: #FFDFDF; + border-color: #FFBBBB; + padding: 15px; + border: 1px solid #DA1D1D; +} + +.topMessage { + background: #E4EEF1; + border-bottom: 1px solid white; + padding: 5px; + font-size: 1.3em; + color: #255E6E; + font-family: 'PT Sans', serif; + text-shadow: #fff 0px 1px 1px; + box-shadow: 1px 1px 1px 1px #ccc; +} + +.topMessage p { + margin-bottom: 0px; +} + +#header { + margin-top: 40px; +} + +span.message { + font-weight: bold; + color: #CE332C; +} + +.gsc-control-cse { + padding: 0px !important; +} + +#gcse { + width: 100%; + height: 60px; +} + +input.gsc-search-button, +input.gsc-search-button:hover, +input.gsc-search-button:focus { + background: #C43422 !important; + border: none !important; +} + +table.gsc-search-box td { + padding: 2px !important; +} + +.gsc-input-box { + border-color: #D3E9EF !important; +} + +.gs-visibleUrl, +.gs-visibleUrl-long { + color: #A5A5A5 !important; +} + +.roundedButton-margin { + margin-left: 5px; +} + +/*! + * Start Bootstrap - Landing Page Bootstrap Theme (http://startbootstrap.com) + * Code licensed under the Apache License v2.0. + * For details, see http://www.apache.org/licenses/LICENSE-2.0. + */ +body, +html { + width: 100%; + height: 100%; +} + + +h1 { + color: #2A4E68; + font-size: 34px; + font-weight: 300; + margin: 40px 0; + text-align: center; +} + +h3 { + color: #2A4E68; +} + +h5 { + color: #DA422F; + margin-bottom: 0; +} + +a, +a:visited { + color: #1080A7; +} + +.lead { + font-size: 18px; + font-weight: 400; +} + +/*Header Navigation more styles in navigation_menu.css */ + +.search-container { + position: relative; + display: none; + float: left; + width: 84%; +} + +@media (min-width: 768px) and (max-width: 1200px) { + .search-container { + width: 80%; + } +} + +@media (max-width: 767px), only screen and (max-device-width: 1024px) { + .search-container { + display: none; + } +} + +.search-icon { + display: none; + padding: 17px 20px 16px; + float: left; + text-transform: uppercase; + color: #8a959e; + font-size: 12px; + font-weight: 400; +} + +.search-icon:hover { + cursor: pointer; + color: #8a959e; +} + +.search-icon i { + margin-right: 5px; + color: #8a959e; +} + +@media (max-width: 767px), only screen and (max-device-width: 1024px) { + .search-icon { + display: none !important; + } +} + +@media (max-width: 1040px) { + .header-search-text { + display: none; + } +} + +@media (max-width: 767px), only screen and (max-device-width: 1024px) { + .header-search-form { + display: none; + } + + .custom-search-box { + position: relative !important; + left: 0px !important; + } +} + +.custom-search-box { + color: #30739C !important; + font-size: 12px !important; + text-transform: lowercase !important; + font-weight: 400 !important; + width: 100% !important; +} + +.custom-search-box-mobile { + display: none !important; + position: relative; + width: 100% !important; +} + +@media (max-width: 767px), only screen and (max-device-width: 1024px) { + .custom-search-box-mobile { + display: block !important; + } + + .custom-search-box { + display: none !important; + } +} + +.header-search, +.custom-search-box, +.custom-search-box-mobile { + border: 2px solid #dae5ee !important; + border-radius: 4px !important; + height: 37px !important; + margin: 7px 0 0 0 !important; + padding: 0 !important; + padding-left: 10px !important; + background: #fff !important; + width: 100% !important; + -webkit-transition: width 4s !important; + transition: width 4s !important; + -webkit-border-horizontal-spacing: 0 !important; + -webkit-border-vertical-spacing: 0 !important; +} + +.header-search contenteditable .custom-search-box-mobile:hover, +.custom-search-box-mobile:focus, +.custom-search-box:hover, +.custom-search-box:focus { + box-shadow: none !important; +} + +.header-search::-webkit-input-placeholder, +.custom-search-box::-webkit-input-placeholder, +.custom-search-box-mobile::-webkit-input-placeholder { + color: #C0CDDB; + font-size: 12px; + text-transform: lowercase; + font-weight: 400; +} + +.header-search:-moz-placeholder, +.custom-search-box-mobile:-moz-placeholder, +.custom-search-box:-moz-placeholder { + /* Firefox 18- */ + color: #C0CDDB; + font-size: 12px; + text-transform: lowercase; + font-weight: 400; +} + +.header-search::-moz-placeholder, +.custom-search-box::-moz-placeholder, +.custom-search-box-mobile::-moz-placeholder { + /* Firefox 19+ */ + color: #C0CDDB; + font-size: 12px; + text-transform: lowercase; + font-weight: 400; +} + +.header-search:-ms-input-placeholder, +.custom-search-box:-ms-input-placeholder, +.custom-search-box-mobile:-ms-input-placeholder { + color: #C0CDDB; + font-size: 12px; + text-transform: lowercase; + font-weight: 400; +} + +.header-search:focus, +.custom-search-box:focus, +.custom-search-box-mobile:focus { + border-radius: 4px; + outline: none; + border: 2px solid #30739C !important; + box-shadow: none; +} + +.close-search { + position: absolute; + top: 20px; + right: 10px; + color: #dae5ee; + z-index: 1001; + font-size: 16px; +} + +.close-search:hover { + color: #30739C; + cursor: pointer; +} + +@media (max-width: 767px), only screen and (max-device-width: 1024px) { + .close-search { + display: none !important; + } +} + +.show { + display: block; +} + +@media (max-width: 767px), only screen and (max-device-width: 1024px) { + .show { + display: none; + } +} + +/*End Header Navigation*/ +/*Hero*/ +.intro-header { + padding-top: 0px; + padding-bottom: 0; + text-align: center; + color: #f8f8f8; + background: url('/themes/openstack/images/hero-bkgd1.jpg') no-repeat center center; + background-size: cover; + position: relative; +} + +.intro-header h1 { + color: #f8f8f8; +} + +.intro-message { + position: relative; + padding-top: 15px; + padding-bottom: 0; +} + +@media (max-width: 1199px) { + .intro-message { + padding-bottom: 0px; + } +} + +.intro-message > h1 { + margin: 0; + font-size: 2.3em; + font-weight: 400; + text-align: center; + font-family: "Open Sans", Helvetica, Arial, sans-serif; + width: 100%; + letter-spacing: -1px; +} + +@media (max-width: 767px) { + .intro-message > h1 { + font-size: 1.5em; + } +} + +.intro-divider { + width: 400px; + border-top: 1px solid #f8f8f8; + border-bottom: 1px solid rgba(0, 0, 0, 0.2); +} + +.intro-message > h3 { + text-shadow: none; + text-align: left; + font-weight: 300; + font-size: 24px; + margin-top: 10px; +} + +.hero-credit { + position: absolute; + bottom: 30px; + right: 30px; + color: #fff; + opacity: 0.5; +} + +.hero-credit:hover { + opacity: 1; +} + +.hero-credit a, +.hero-credit a:hover { + color: #fff; +} + +/*Hero Promo, add .featured to .intro-header*/ +.intro-header.featured { + text-align: center; + background: no-repeat scroll center center / cover rgba(0, 0, 0, 0); + min-height: 300px; + background-size: cover; + position: relative; + padding: 60px 0; +} + +.intro-header.featured .intro-message { + padding-bottom: 5px; +} + +a.promo-btn { + white-space: nowrap; + color: white; + font-size: 16px; + font-weight: 400; + background: #DA422F; + border-radius: 4px; + padding: 7px 60px; + margin: 0 auto; + text-align: center; + min-width: 250px; + display: inline-block; + line-height: 2; +} + +a.promo-btn:hover, +a.promo-btn:focus { + text-decoration: none; + background: #A51B1B; +} + +.promo-btn-wrapper { + margin-bottom: 10px; +} + +a.promo-btn i.fa-chevron-right { + background: transparent; + border-radius: 50%; + padding: 0; + font-size: 13px; + margin-left: 20px; + line-height: 1em; +} + +p.promo-dates { + display: inline-block; + margin-top: 10px; + font-weight: 400; + margin-bottom: 30px; + letter-spacing: -.4px; +} + +/*End Hero*/ +/*Overview Section*/ +.overview-section { + padding: 80px 0; +} + +@media (max-width: 767px) { + .overview-section { + padding-top: 40px; + } +} + +.overview-section h2 { + color: #2A4E68; + font-size: 34px; + font-weight: 300; + margin-bottom: 25px; +} + +.overview-section p { + color: #888; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} + +.overview-section a { + color: #30739C; + text-decoration: underline; +} + +.btn-wrapper { + float: left; + width: 100%; + text-align: center; +} + +@media (max-width: 980px) { + .overview-left { + margin-bottom: 50px; + } +} + +a.overview-btn { + float: left; + background: #30739C; + color: #fff; + text-transform: uppercase; + border-radius: 4px; + padding: 15px 25px; + text-decoration: none; + margin-top: 5px; + margin-bottom: 5px; +} + +a.overview-btn:hover { + background: #2A4E68; +} + +a.overview-btn.left-btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: 1px solid #3387C9; + margin-right: 0; +} + +a.overview-btn.left-btn:hover { + border-right-color: #1B486B; +} + +a.overview-btn.right-btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left: 1px solid #1B486B; + margin-left: 0; +} + +.release-text { + display: block; + text-align: left; + color: #636568; + font-size: 13px; +} + +.overview-section .img-responsive { + max-width: 550px; +} + +.overview-right { + text-align: center; +} + +.control-cloud-graphic { + max-width: 600px; + margin: 0 auto 15px; +} + +a.demo-link { + font-size: 13px; + font-weight: 400; + text-decoration: none; + color: #34789A; + background: #F0F9FE; + padding: 10px 25px; + border-radius: 4px; +} + +a.demo-link:hover { + color: #34789A; + background: #CDD9E2; +} + +a.demo-link i { + margin-left: 5px; + font-size: 14px; +} + +/*When Featured, Add Class .featured to .overview-section*/ +.overview-section.featured .overview-right { + background: #E8EEF5; + border-radius: 4px; + text-align: center; + padding: 20px; + margin-top: 50px; +} + +@media (max-width: 1200px) { + .overview-section.featured .overview-right { + margin-top: 50px; + } +} + +.overview-section.featured .overview-right h3 { + color: #30739C; + text-align: center; +} + +.overview-section.featured .overview-right p { + color: #333; + font-size: 13px; +} + +.overview-section.featured .overview-right img.promo-graphic { + max-width: 350px; + margin: 20px auto; +} + +/*End Overview Section*/ +/*Customers Section*/ +.customers-row { + padding: 80px 0; + text-align: center; + background: #edf2f7; +} + +.customers-row h2 { + text-transform: capitalize; + margin-bottom: 0; + text-align: center; +} + +.customer-logos-wrapper { + margin: 15px 0; + text-align: center; +} + +.customer-logos { + position: relative; + padding: 20px 0; + width: 15%; + display: inline-block; +} + +@media (max-width: 767px) { + .customer-logos { + position: relative; + padding: 20px 0; + width: 80%; + margin: 0 auto; + } +} + +.logo-hover { + background: #dee2e8; + border-radius: 3px; +} + +.logo-hover:after { + content: ' '; + height: 0; + position: absolute; + width: 0; + border: 10px solid transparent; + border-top-color: #dee2e8; + top: 100%; + left: 50%; + margin-left: -10px; +} + +.customers-description { + margin: 40px 0; + color: #2A4E68; + text-align: center; +} + +.customers-description p { + padding: 20px 50px; + border-top: 1px solid #dee2e8; + border-bottom: 1px solid #dee2e8; + display: inline; +} + +@media (max-width: 767px) { + .customers-description p { + display: block; + } +} + +.customers-action { + margin-top: 40px; + text-align: center; +} + +a.customer-btn { + background: #2A4E68; + color: #fff; + text-transform: uppercase; + border-radius: 4px; + padding: 15px 25px; + text-decoration: none; + border-style: none; +} + +a.customer-btn:hover { + background: #173D5B; + color: #fff; +} + +/*When Featured, Add Class .featured to .customers-row*/ +.customers-row.featured .customer-logos-wrapper { + width: 50%; + max-width: 1000px; + margin: 40px 25% 0; + border-top: 1px solid #DDE3E8; + padding-top: 20px; +} + +.customers-row.featured .customer-logos-wrapper hr { + color: #333; +} + +.customers-row.featured .customer-logos img { + width: 90%; + max-width: 100px; +} + +.customers-row.featured .customer-logos:hover { + background: none; +} + +.customers-row.featured .customer-logos:hover:after { + display: none; +} + +.customers-row.featured .customers-action { + margin-top: 0; +} + +.customers-row.featured button.customer-btn { + background: none; + border-radius: 0; + padding: 0; + text-decoration: underline; + color: #2A4E68; + text-transform: capitalize; +} + +.featured-description { + width: 85%; + margin: 40px auto; +} + +@media (max-width: 767px) { + iframe { + width: 90%; + } +} + +/*End Customers Section*/ +/*Community Section*/ +.community-section { + padding: 75px 0; + background: url('/themes/openstack/images/community-bkgd-map.jpg') no-repeat 40% center; + background-size: cover; + min-height: 350px; +} + +.community-section.featured { + background: url('/themes/openstack/images/community-bkgd2.jpg') no-repeat center center; + background-size: cover; +} + +.community-graphic { + max-width: 650px; + margin: 0 auto; +} + +@media (max-width: 994px) { + .community-graphic { + margin-bottom: 20px; + } +} + +.community-section h2 { + color: #fff; +} + +.community-section p { + color: #fff; +} + +@media (max-width: 994px) { + .community-section h2 { + text-align: center; + } + .community-section p { + text-align: center; + } +} + +a.community-btn { + font-size: 16px; + font-weight: 400; + background: transparent; + border: 1px solid #fff; + border-radius: 3px; + margin-top: 20px; + padding: 8px 30px 8px 40px; + color: white; + display: block; + max-width: 220px; + +} +@media (max-width: 994px) { + a.community-btn { + display: block; + max-width: 220px; + margin: 20px auto 0; + } +} + +a.community-btn i { + margin-left: 10px; +} + +a.community-btn:hover { + text-decoration: none; + background: rgba(255, 255, 255, 0.2); +} + +/*When Featured, add .featured to .community-section*/ +.community-section.featured h3 { + color: #fff; +} + +@media (max-width: 1200px) { + .community-section.featured h3 { + text-align: center; + } +} + +.designate-logo { + max-width: 302px; + margin: 50px 0 20px; +} + +@media (max-width: 1200px) { + .designate-logo { + margin: 50px auto 20px; + } +} + +.default-community { + background-color: rgba(5, 54, 86, 0.7); + border-radius: 4px; + padding: 30px; + text-align: center; +} + +@media (max-width: 1200px) { + .default-community { + margin-top: 50px; + } +} + +.default-community h2 { + margin-top: 0; +} + +.community-graphic.small { + max-width: 450px; + margin: 20px auto; +} + +a.featured-link { + display: block; + background: #DA422F; + padding: 10px 20px; + border-radius: 2px; + width: 80%; + text-decoration: none; + margin: 10px auto 0; + color: #fff; + font-weight: 600; +} + +a.featured-link:hover { + text-decoration: none; + background: #831917; +} + +/*End Community Section*/ +/*News and Events Section*/ +.news-section { + padding: 70px 0; +} + +.news-section h2 a { + font-size: 14px; + color: #30739C; + font-weight: 400; + margin-left: 30px; +} + +.news-section h2 a:hover { + color: #28709a; + text-decoration: none; +} + +.event-ad, +.news-ad { + width: 100%; + max-width: 560px; + margin-bottom: 20px; +} + +.event-ad-lrg { + width: 100%; + height: 113px; + max-height: 113px; + max-width: 1140px; + margin-bottom: 20px; + image-rendering: optimizeQuality; +} + +@media (max-width: 767px) { + .event-ad-lrg { + max-width: 767px; + } +} + +.news-section .news-wrapper ul { + margin: 30px 0; + -moz-padding-start: 0; + -webkit-padding-start: 0; +} + +.news-section .news-wrapper ul li { + list-style: none; +} + +.single-event { + float: left; + width: 100%; + padding: 15px 10px; + border-bottom: 1px solid #ebeff4; +} + +.single-event:hover { + background: #edf2f7; +} + +.single-event.last { + border-bottom: none; +} + +.left-event { + float: left; + width: 20%; +} + +@media (min-width: 768px) and (max-width: 981px) { + .left-event { + width: 25%; + } +} + +@media (max-width: 767px) { + .left-event { + width: 15%; + } +} + +.event-details { + float: left; + margin-left: 3%; + width: 62%; +} + +@media (min-width: 768px) and (max-width: 981px) { + .event-details { + width: 72%; + } +} + +.right-event { + float: right; + width: 13%; +} + +@media (min-width: 768px) and (max-width: 981px) { + .right-event { + display: none; + } +} + +.date, +.news-type, +.planet-type { + background: #fff; + border: 2px solid #DA422F; + border-radius: 4px; + padding: 5px 0; + color: #DA422F; + font-size: 10px; + width: 100%; + text-align: center; + float: left; + margin-top: 5px; +} + +.event-name, +.news-title { + display: block; + font-size: 14px; + font-weight: 600; + color: #333; + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.location, +.news-date { + display: block; + float: left; + font-style: italic; + color: #333; + font-weight: 300; +} + +.right-arrow { + float: right; + background: #d9dfe5; + border-radius: 100px; + width: 30px; + height: 30px; + padding: 6px 10px; + color: #fff; + margin-right: 10px; + margin-top: 5px; + display: none; +} + +.single-event:hover .right-arrow { + display: block; +} + +.news-type { + border: 2px solid #2A4E68; + color: #2A4E68; +} + +.planet-type { + border: 2px solid #68C8C3; + color: #68C8C3; +} + +.see-more-bottom { + width: 100%; + float: left; + text-align: center; + margin-top: 40px; +} + +.see-more-bottom a { + color: #2A4E68; + text-decoration: none; + padding: 5px 15px; + border-radius: 4px; + font-size: 12px; + font-weight: 400; + border: 2px solid #2A4E68; +} + +.see-more-bottom a i { + margin-left: 10px; + font-size: 11px; +} + +/*End News and Events Section*/ +/*Photo Row*/ +.photo-row-wrapper { + width: 100%; + color: #eee; + max-height: 240px; + margin-bottom: 10px; + overflow: hidden; + margin-top: 60px; +} + +@media (max-width: 765px) { + .photo-row-wrapper { + display: none; + } +} + +.photo-container { + width: 4000px; + max-height: 240px; + margin-left: -30px; +} + +.photo-container img { + margin: 0 10px 0 0; + max-width: 22%; + max-height: 240px; +} + +/*End Photo Row*/ +/*Footer*/ + +.footer-links h3 { + color: #fff; + font-size: 14px; + font-weight: 400; +} + +.footer-links ul { + margin-left: 0; + -moz-padding-start: 0; + -webkit-padding-start: 0; +} + +.footer-links ul li a { + color: #aaa; + font-size: 12px; + font-weight: 400; + list-style: none; + margin-left: 0; +} + +.social-icons { + width: 40px; + min-height: 40px; + display: inline-block; + margin-right: 10px; +} + +.footer-twitter { + background: url('/themes/openstack/images/footer-twitter.png') no-repeat; +} + +.footer-twitter:hover { + background: url('/themes/openstack/images/footer-twitter-hover.png') no-repeat; +} + +.footer-facebook { + background: url('/themes/openstack/images/footer-facebook.png') no-repeat; +} + +.footer-facebook:hover { + background: url('/themes/openstack/images/footer-facebook-hover.png') no-repeat; +} + +.footer-linkedin { + background: url('/themes/openstack/images/footer-linkedin.png') no-repeat; +} + +.footer-linkedin:hover { + background: url('/themes/openstack/images/footer-linkedin-hover.png') no-repeat; +} + +.footer-youtube { + background: url('/themes/openstack/images/footer-youtube.png') no-repeat; +} + +.footer-youtube:hover { + background: url('/themes/openstack/images/footer-youtube-hover.png') no-repeat; +} + +.newsletter-form { + margin: 10px 0 30px; + width: 100%; +} + +.newsletter-form label { + color: #aaa; + font-size: 12px; + font-weight: 300; + display: block; +} + +.newsletter-input { + display: inline-block; + background: transparent; + border: 2px solid #888; + border-radius: 4px; + color: #888; + font-size: 12px; + font-weight: 400; + padding: 10px 15px; + width: 70%; +} + +@media (max-width: 767px) { + .newsletter-input { + width: 70%; + } +} + +.newsletter-input::-webkit-input-placeholder { + color: #888; + font-size: 12px; + font-weight: 400; + text-transform: uppercase; +} + +.newsletter-input:-moz-placeholder { + /* Firefox 18- */ + color: #888; + font-size: 12px; + font-weight: 400; + text-transform: uppercase; +} + +.newsletter-input::-moz-placeholder { + /* Firefox 19+ */ + color: #888; + font-size: 12px; + font-weight: 400; + text-transform: uppercase; +} + +.newsletter-input:-ms-input-placeholder { + color: #888; + font-size: 12px; + font-weight: 400; + text-transform: uppercase; +} + +.newsletter-input:focus { + outline: none; + border: 2px solid #666; +} + +.newsletter-btn { + margin-left: 1%; + display: inline-block; + background: transparent; + border: 2px solid #30739C; + border-radius: 4px; + color: #30739C; + font-size: 12px; + font-weight: 400; + padding: 10px 15px; + text-transform: uppercase; + width: 27%; +} + +@media (max-width: 767px) { + .newsletter-btn { + width: 18%; + margin-left: 2%; + padding: 10px; + } +} + +.newsletter-btn:hover { + border: 2px solid #888; + color: #999; +} + +.fine-print { + margin-top: 20px; + color: #aaa; + font-size: 12px; +} + +.fine-print a { + color: #aaa; + text-decoration: underline; +} + +.fine-print a:hover { + color: #fff; +} + +.footer-bottom { + background: #222; + padding: 15px 0; + text-align: center; + width: 100%; +} + +.feedback-input { + display: inline-block; + background: #222; + border: 2px solid #444; + border-radius: 4px; + color: #777; + font-size: 12px; + font-weight: 400; + padding: 10px 20px; + width: 310px; +} + +@media (max-width: 767px) { + .feedback-input { + width: 70%; + } +} + +.feedback-input::-webkit-input-placeholder { + color: #555; + font-size: 12px; + font-weight: 400; +} + +.feedback-input:-moz-placeholder { + /* Firefox 18- */ + color: #555; + font-size: 12px; + font-weight: 400; +} + +.feedback-input::-moz-placeholder { + /* Firefox 19+ */ + color: #555; + font-size: 12px; + font-weight: 400; +} + +.feedback-input:-ms-input-placeholder { + color: #555; + font-size: 12px; + font-weight: 400; +} + +.feedback-input:focus { + outline: none; + border: 2px solid #666; +} + +.feedback-btn { + margin-left: 11px; + display: inline-block; + background: #222; + border: 2px solid #666; + border-radius: 4px; + color: #777; + font-size: 12px; + font-weight: 400; + padding: 10px 30px; +} + +@media (max-width: 767px) { + .feedback-btn { + width: 18%; + margin-left: 2%; + padding: 10px; + } +} + +.feedback-btn:hover { + border: 2px solid #888; + color: #999; +} + +/*End Footer*/ +/* Line below navigation */ +.navbar-default { + border-bottom: 1px solid #ddd; +} + +#home .navbar-default { + border-bottom: none; +} + +#home .top-site-banner { + display: none; +} + +/*End General Inner Page Styles*/ +/*Events Page*/ +.eventsBanner { + height: 150px; + padding: 20px; +} + +.eventsPhotoCaption { + background: rgba(0, 0, 0, 0.3); + border-radius: 4px; + padding: 10px; + color: white; +} + +.news-section.full { + padding: 20px 0; +} + +.eventTitleArea { + text-align: center; + margin: 40px 0; +} + +.eventTitleArea h1 { + color: #333; + font-weight: 300; +} + +.postEvent { + float: left; + width: 100%; + padding: 30px 5%; + background: #F4F5F8; + margin: 20px 0 10px; + text-align: center; +} + +.postEvent p { + margin-bottom: 20px; +} + +.postEvent a { + background: #2A4E68; + padding: 10px 25px; + border-radius: 4px; + color: #fff; +} + +.eventBlock { + float: left; + width: 100%; +} + +.eventBlock.summit .date { + border-color: #2A4E68; + color: #2A4E68; +} + +.eventBlock.past h2 { + margin-top: 50px; +} + +.eventBlock.past .date { + border-color: #68C8C3; + color: #68C8C3; +} + +/*End Events Page*/ +/*Community Page*/ +.communityBoxes { + margin: 30px 0 10px; + font-size: 13px; +} + +.communityBoxes h2 { + font-size: 20px; + font-weight: 400; + margin-bottom: 15px; +} + +.communityBoxes h2 a { + color: #DA422F; +} + +.developersRow { + border-top: 1px solid #eee; + border-bottom: 1px solid #eee; + padding: 20px 30px; + margin-bottom: 30px; +} + +.devLabel { + float: left; + margin-right: 30px; + font-weight: 700; +} + +ul#developerActivity { + float: left; + padding-left: 0; + margin: 0; +} + +ul#developerActivity li { + list-style: none; + display: inline-block; +} + +ul#developerActivity li a { + font-weight: 700; + color: #222; +} + +ul#developerActivity li span { + background: #E8EEF5; + padding: 5px 10px; + border-radius: 4px; + margin-right: 5px; + color: #2A4E68; + font-size: 11px; + font-weight: 400; +} + +/*End Community Page*/ +/*Sofware Page*/ +.software { + margin-top: 30px; +} + +.software-top { + text-align: center; +} + +.software-top h1 { + margin: 5px 0 10px; +} + +p.icon { + text-align: center; + margin: 40px 0 0; +} + +p.software-description { + text-transform: uppercase; + font-size: 13px; + color: #777; +} + +.openstack-diagram { + margin-bottom: 3em; +} + +div.screenshots { + padding: 30px 0 20px; + margin: 30px 0; + border-top: 1px solid #edf2f7; + border-bottom: 1px solid #edf2f7; + width: 100%; +} + +.screenshots ul { + padding: 0; + margin: 0; + text-align: center; +} + +.screenshots ul li { + list-style: none; + display: inline-block; +} + +@media (max-width: 767px) { + .screenshots ul li { + margin-bottom: 30px; + } +} + +.screenshots ul li a img { + width: 100%; + border: 4px solid #edf2f7; +} + +.screenshots ul li a img:hover { + border-color: #30739C; +} + +.screenshots ul li p { + margin: 10px 0 0; + font-size: 12px; + color: #30739C; + text-align: center; +} + +.newSubNav li#start a.current, +.newSubNav li#start a.current:hover { + color: #488613; + background-color: #DDFFE2; +} + +ul.slides h3 { + text-align: center; + margin: 0 0 30px; +} + +.tabSet { + background: #edf2f7; + border: 1px solid #30739C; + margin: 30px 0; + padding-top: 20px; + padding-bottom: 20px; +} + +.tabContent table { + width: 100%; + margin: 0; + table-layout: fixed; + word-wrap: break-word; +} + +.get-started-wrapper h3 { + text-align: center; + margin: 40px 0; +} + +a.start-btn { + background: #30739C; + color: #fff; + text-transform: capitalize; + font-size: 12px; + border-radius: 2px; + padding: 10px 15px; + text-decoration: none; + border-style: none; + display: inline-block; +} + +img.deploy-powered { + margin-top: 40px; +} + +@media (max-width: 767px) { + img.deploy-powered { + margin: 10px 0 30px; + } +} + +img.deploy-compatible { + margin-top: 30px; +} + +@media (max-width: 767px) { + img.deploy-compatible { + margin: 10px 0 30px; + } +} + +img.icehouse-video { + max-width: 100%; +} + +h1.release { + text-align: left; +} + +.documentation { + margin-top: 50px; + text-align: center; + background: #edf2f7; + border-radius: 4px; + padding: 30px 0; +} + +.documentation a { + padding: 7px 20px; + background: #30739C; + color: #edf2f7; + margin: 0 8px; + border-radius: 4px; + display: inline-block; +} + +.documentation a:hover { + text-decoration: none; + background: #2A4E68; +} + +@media (max-width: 767px) { + .documentation a { + padding: 10px; + margin: 5px; + font-size: 14px; + display: block; + } +} + +/*End Sofware Page*/ +/*Marketplace Page*/ +h2.marketplace-header { + border-left: 3px solid #DA422F; + line-height: 1.2em; + margin: 30px 20px; + padding-left: 15px; + font-size: 1.5em; +} + +.build-use-box { + background: #edf2f7; + border-radius: 4px; + margin-bottom: 30px; + min-height: 25em; + padding: 20px; +} + +.build-use-box h3 { + margin-bottom: 20px; +} + +.build-use-box ul { + padding-left: 20px; +} + +.marketplace-description { + margin-top: 50px; +} + +.video { + width: 100%; +} + +.video iframe { + width: 100%; + height: 225px; +} + +.program-logos { + border-top: 1px solid #eee; + margin-top: 60px; + padding-top: 60px; +} + +.ecosystem-wrapper { + padding-left: 30px; + border-left: 1px solid #eee; +} + +.marketplace-top-wrapper { + padding: 20px 0 20px; +} + +.marketplace-brand { + margin-top: 20px; + padding-left: 30px; +} + +h2.marketplace { + margin-bottom: -7px; + margin-top: 0; + font-size: 18px; + font-family: 'PT Sans', serif; + font-style: normal; + letter-spacing: -0.076em; + line-height: 1em; +} + +h2.marketplace a, +h2.marketplace a:hover { + color: #5B83A0; + text-decoration: none; +} + +h1.marketplace { + font-size: 24pt; + margin-top: 5px; + text-align: left; +} + +h1.marketplace:hover { + text-decoration: none; +} + +h1.marketplace a, +h1.marketplace a.visited { + color: #264D69; + font-family: 'PT Sans', serif; + font-style: normal; + letter-spacing: -0.076em; + line-height: 1em; +} + +.grey-bar { + background-color: #edf2f7; + margin-bottom: 40px; + margin-top: 0; + padding-bottom: 10px; + padding-top: 10px; +} + +ul.marketplace-nav { + padding: 0; + margin: 0; +} + +ul.marketplace-nav li { + border-right: 1px solid #e8e8e8; + display: inline-block; + padding-left: 0.5em; + padding-right: 0.5em; + text-transform: uppercase; + width: 130px; + height: 96px; + vertical-align: top; + text-align: center; +} + +ul.marketplace-nav li:last-child { + border-right: none; +} + +@media (min-width: 680px) and (max-width: 1000px) { + ul.marketplace-nav li { + width: 100px; + height: 110px; + } + + ul.marketplace-nav li a { + font-size: 10px; + } +} + +@media (max-width: 680px) { + ul.marketplace-nav li { + display: table-cell; + border-right: none; + text-align: left; + border-bottom: 1px solid #e8e8e8; + width: 100%; + height: auto; + padding: 5px 10px; + vertical-align: middle; + } +} + +ul.marketplace-nav li a { + color: #848575; + text-decoration: none; + font-size: 12px; + vertical-align: middle; + width: 115px; + text-align: center; +} + +@media (max-width: 680px) { + ul.marketplace-nav li a { + padding: 0; + vertical-align: middle; + float: left; + width: 100%; + text-align: left; + } +} + +ul.marketplace-nav a:hover { + color: #DA422F; +} + +ul.marketplace-nav a span { + background: url("/marketplace/code/ui/frontend/images/marketplace-icons.png") no-repeat scroll 0 0 rgba(0, 0, 0, 0); + display: block; + height: 50px; + margin: auto; + width: 40px; + text-align: center; +} + +@media (max-width: 680px) { + ul.marketplace-nav a span { + display: table-cell; + vertical-align: middle; + } +} + +ul.marketplace-nav #training a span { + background-position: 0 0; +} + +ul.marketplace-nav #distros a span { + background-position: -50px 0; +} + +ul.marketplace-nav #public-clouds a span { + background-position: -150px 0; +} + +ul.marketplace-nav #private-clouds a span { + background-position: -250px 0; +} + +ul.marketplace-nav #consulting a span { + background-position: -100px 0; +} + +ul.marketplace-nav #drivers a span { + background-position: -200px 0; +} + +ul.marketplace-nav .current a { + color: #DA422F; +} + +/*End Marketplace Page*/ +/*Marketplace Listing Page*/ +.product-box { + border: 1px solid #E8E8E8; + margin-bottom: 30px; + padding: 30px 20px; + border-radius: 4px; + min-height: 200px; + border-left: 3px solid #DA422F; +} + +.logo-area { + padding-top: 20%; + padding-left: 10px; +} + +@media (max-width: 767px) { + .logo-area { + padding-top: 0; + padding-left: 0; + } +} + +.company-details-area h4 { + text-transform: uppercase; + font-weight: 300; +} + +.tested-listing { + border-top: 1px solid #E8E8E8; + border-bottom: 1px solid #E8E8E8; + padding: 10px 5px; + margin: 20px 0 15px; +} + +.tested-listing i.fa-check-square { + color: #30c530; + font-size: 1.3em; + display: inline-block; + line-height: 21px; +} + +.tested-listing .tested-listing-title { + color: #30c530; + text-transform: uppercase; + font-weight: 600; + font-size: 1em; + display: inline-block; + margin-left: 2px; + vertical-align: top; +} + +.tested-listing .tested-listing-description { + display: inline-block; + font-style: italic; + font-size: .85em; + margin-left: 5px; +} + +.details-button { + background: url("/marketplace/code/ui/frontend/images/register-arrow.png") no-repeat scroll 85% center #000000; + color: #FFFFFF !important; + display: inline-block; + margin-right: -10px; + padding: 10px 2%; + text-align: left; + text-transform: uppercase; + width: 100px; + font-size: 12px; + border-radius: 4px; +} + +.filter-label { + background: url("/themes/openstack/images/marketplace/search-icon.png") no-repeat scroll left 7px rgba(0, 0, 0, 0); + color: #30739C; + float: left; + margin-bottom: 0; + margin-top: 5px; + padding-left: 25px; + padding-right: 20px; + padding-top: 8px; + text-transform: uppercase; + font-size: 12px; +} + +input#name-term { + height: 40px; + width: 250px; + font-size: 12px; + padding: 5px 10px; + border: 1px solid #e8e8e8; + color: #30739C; + border-radius: 4px; +} + +input#name-term::-webkit-input-placeholder { + color: #30739C; + font-size: 12px; +} + +select#service-term { + height: 40px; + width: 200px; + padding: 5px 10px; + position: relative; + border-radius: 4px; + color: #30739C; + background: #fff; + border: 1px solid #e8e8e8; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + display: inline-block; + cursor: pointer; + font-size: 12px; + font-weight: 400; +} + +label#service-label { + position: relative; +} + +label#service-label:after { + content: '>'; + color: #30739C; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); + right: 14px; + top: 6px; + padding: 0 0 6px; + border-bottom: 1px solid #e8e8e8; + position: absolute; + pointer-events: none; + font-size: 14px; + font-weight: 400; +} + +label#service-label:before { + content: ''; + right: 6px; + top: 0px; + width: 20px; + height: 20px; + background: #f8f8f8; + position: absolute; + pointer-events: none; + display: block; +} + +@media (max-width: 767px) { + .filter-label { + width: 100%; + margin-bottom: 10px; + } + + input#name-term { + display: block; + width: 100%; + margin-bottom: 10px; + } + + select#service-term { + min-width: 400px; + } +} + +ul.resource-links { + padding: 0; + margin: 0 0 30px; +} + +ul.resource-links li { + list-style: none; + display: block; +} + +ul.resource-links li a { + background-color: #edf2f7; + padding: 15px 15px; + margin: 0 0 5px; + display: block; + border-radius: 4px; +} +ul.resource-links > li > a:after { + position: absolute; + font-family: FontAwesome; + margin-top: 0px; + right: 35px; + content: "\f138"; + font-size: 1.2em; +} + +ul.resource-links li a:hover { + text-decoration: none; + background-color: #DAE1EB; +} + +.add-your-course { + border-radius: 4px; + border: 1px solid #30739C; + background: #fff; + padding: 20px; + font-size: 13px; +} + +.foundation-report-title { + font-size: 1.1em; + font-weight: 300; + margin: 0 0 15px; + position: relative; + display: inline-block; +} + +.foundation-report-title:before, +.foundation-report-title:after { + content: ""; + position: absolute; + border-bottom: 1px solid white; + top: 13px; + width: 80px; +} + +.foundation-report-title:before { + right: 100%; + margin-right: 10px; +} + +.foundation-report-title:after { + left: 100%; + margin-left: 10px; +} + +/*404 Page*/ +.four-o-four-wrapper { + text-align: center; + margin: 50px auto; +} + +.four-o-four-wrapper img { + width: 100%; + max-width: 650px; + margin: 50px 0 70px; +} + +.four-o-four-wrapper p { + color: #8a959e; +} + +.four-o-four-wrapper a { + color: #30739C; + text-decoration: none; +} + +.four-o-four-links { + margin-top: 50px; +} + +.four-o-four-links a { + color: #30739C; + padding: 0 10px; + text-decoration: none; +} + +@media (max-width: 767px) { + .four-o-four-links a { + padding: 0 5px; + } +} + +/*End 404 Page*/ +/*Blog Page*/ +.blog-title { + width: 100%; + background: url(/themes/openstack/images/images/summit-promo-bkgd1.jpg) no-repeat center center; + background-size: cover; + padding: 50px 0; + text-align: center; +} + +.blog-o { + max-width: 80px; + opacity: 0.9; +} + +.blog-title h1 { + color: white; + font-weight: 400; + font-size: 45px; + margin: 10px 0; +} + +.blog-title h1 span { + font-size: 16px; + display: block; + padding-top: 10px; + color: white; + font-weight: 300; +} + +.container.blog { + margin-top: 30px; +} + +.post { + margin-bottom: 60px; +} + +.post-byline { + border-bottom: 1px dotted #c5e2ea; + border-top: 1px dotted #c5e2ea; + margin-bottom: 15px; + padding: 12px 0; + font-size: 12px; +} + +.post-byline p { + margin-bottom: 0; +} + +.post-byline .name a { + color: #DA422F; +} + +.post-byline .postDate { + text-align: right; + color: #2A4E68; +} + +.entry h1, +.entry h2, +.entry h3, +.entry h3 a { + color: #2A4E68; + font-weight: 300; +} + +.entry h1 { + font-size: 24px; +} + +.entry h2 { + font-size: 20px; +} + +.entry h3 { + font-size: 18px; +} + +.entry h3 a { + text-decoration: underline; +} + +#sidebar { + background: #edf2f7; + padding: 15px; + border-radius: 4px; + margin-top: 0; +} + +#sidebar ul { + margin: 0; +} + +#sidebar ul li { + font-size: 12px; +} + +.creative-commons { + text-align: center; + margin-top: 40px; + font-size: 12px; +} + +.creative-commons img { + margin-bottom: 10px; +} + +.navigation a { + background: #30739C; + padding: 5px 10px; + border-radius: 4px; + color: white; +} + +/*End Blog Page*/ + +/* site banner */ +.top-site-banner { + background-color: #1080A7; + color: #fff; + padding-top: 10px; +} + +a.top-site-banner-button { + border-radius: 4px; + font-size: 0.85em; + padding: 3px 20px; + background-color: #30739C; + display: inline-block; + color: white; + text-transform: uppercase; +} + +a.top-site-banner-button:hover { + text-decoration: none; + background-color: #2A4E68; + color: white; +} + +a.top-site-banner-button:visited { + color: white; +} + +/* end of site banner */ +/*Paris Summit Page*/ +.conference-title#paris { + margin-top: 30px; + width: 100%; +} + +.conference-title#paris img { + width: 100%; + max-width: 1150px; +} + +.conference-title h2, +.conference-title h1 { + display: none; +} + +.conference-summary { + min-height: 90px; + border-bottom: 1px solid #edf2f7; + padding-top: 30px; + padding-bottom: 30px; +} + +.conference-calendar img { + max-width: 100%; +} + +.news-heading h2 { + margin-bottom: 0px; +} + +.rss-wrapper { + margin: 20px 0 0; +} + +a.rss { + color: #E46616; + background: url("/themes/openstack/images/rss.png") no-repeat left center white; + padding-left: 20px; + float: right; +} + +.conference-calendar { + height: 90px; +} + +.conference-calendar p.date { + display: none; +} + +.register a, +.register a:visited { + display: block; + background: url("/themes/openstack/images/pointer-arrow.png") no-repeat 90% center #FF9631; + color: white; + padding: 10px 20px; + border-radius: 4px; + width: 80%; + font-size: 15px; +} + +.register a:hover { + background-color: #E46616; + text-decoration: none; +} + +.summit-videos, +.summit-videos:visited, +.summit-videos:active { + display: block; + width: 169px; + padding: 6px; + padding-top: 140px; + background: url("/themes/openstack/images/conferences/portland/summit-videos.png") no-repeat top center; + color: black; + text-decoration: none !important; +} + +.summit-videos#atlanta-experience { + background: url("/themes/openstack/images/conferences/paris/atlanta-experience.png") no-repeat top center; +} + +ul.documents { + list-style: none; + padding: 0; +} + +ul.documents li a, +ul.documents li a:visited { + padding: 15px 10px 15px 45px; + display: block; + list-style: none; + background: url("/themes/openstack/images/pdf-icon.png") no-repeat 15px center #edf2f7; + margin: 1px 0px 0px 0px; + color: #30739C; + border-radius: 4px; +} + +#important-dates h3 { + font-size: 18px; +} + +#important-dates { + font-size: 13px; +} + +.news-item { + border-bottom: 1px solid #edf2f7; + padding-bottom: 30px; + margin-bottom: 30px; +} + +/*End Paris Summit Page*/ +.newSubNav { + width: 85%; +} + +.newSubNav ul { + padding: 0px; + margin-top: 30px; + margin-left: -10px; +} + +.newSubNav ul:nth-child(2) { + border-top: 1px solid #E8E8E8; + padding-top: 30px; +} + +.newSubNav li { + list-style: none; + position: relative; +} + +.newSubNav a { + color: #8A959E; + text-transform: uppercase; + font-size: 12px; + padding: 15px 20px; + display: block; +} + +.newSubNav a.current, +.newSubNav a.current:hover { + background-color: #EDF2F7; + font-weight: bold; + color: #2D709B; +} + +.newSubNav a:hover { + text-decoration: none; + color: black; +} + +.newSubNav .fa-chevron-right { + color: #C6D8E4; + position: absolute; + right: 15px; + top: 50%; + margin-top: -6px; +} + +li#start a { + color: #75C320; +} + +li#openstack-shared-services { + padding-bottom: 20px; + border-bottom: 1px solid #E8E8E8; +} + +li#start { + margin-top: 20px; +} + +fieldset:nth-child(1) { + margin-top: 20px; +} + +fieldset { + border: 1px solid #CCCCCC; + padding: 20px 17px 20px 17px; + margin-bottom: 20px; +} + +fieldset ul { + list-style: none; +} + +input[type="radio"] { + margin-left: 0px; +} + +input[type="checkbox"] { + margin-left: 0px; +} + +input[type="checkbox"], +input[type="radio"] { + display: inline-block; +} + +fieldset.footer-feedback-fieldset { + border: 0; + background: transparent; + padding: 0; + margin: 0; +} + +.footer-links ul li { + list-style: none; +} + +/*New responsive nav*/ +.navbar-collapse { + border-top: 0 +} + +@media only screen and (max-device-width: 1024px) { + .navbar-header { + float: none; + } + + .navbar-left,.navbar-right { + float: none !important; + } + + .navbar-toggle { + display: block; + } + + .navbar-fixed-top { + top: 0; + border-width: 0 0 1px; + } + + .navbar-collapse.collapse { + display: none!important; + } + + .navbar-nav { + float: none!important; + margin-top: 7.5px; + } + + .navbar-nav>li { + float: none; + } + + .navbar-nav>li>a { + padding-top: 10px; + padding-bottom: 10px; + } + + .collapse.in { + display: block !important; + } +} + +i.mobile-expand { + display: none; +} + +@media (max-width: 767px) , only screen and (max-device-width: 1024px) { + .navbar-default ul.navbar-main > li > a { + width: auto; + display: inline-block; + } + + i.mobile-expand { + position: relative; + display: inline-block; + cursor: pointer; + } + + i.mobile-expand:after { + position: absolute; + padding: 10px 15px; + line-height: 20px; + color: #8a959e; + top: 50%; + margin-top: 0; + right: 0px; + content: "+"; + font-size: 1.7em; + font-style: normal; + } + + .navbar-nav > li.open i.mobile-expand { + position: relative; + display: inline-block; + cursor: pointer; + } + + .navbar-nav > li.open i.mobile-expand:after { + position: absolute; + padding: 10px 15px; + line-height: 20px; + color: #aaa; + top: 50%; + margin-top: 0; + right: 0px; + content: "-"; + font-size: 3em; + font-style: normal; + font-weight: 300; + } + + .navbar-nav > li > ul.dropdown-menu { + display: block; + max-height: 0; + height: 0; + position: relative; + overflow: hidden; + border: 0; + box-shadow: none; + width: 100%; + padding: 0; + background: #edf2f7; + margin: 0; + -moz-transition: max-height 2s ease; + -webkit-transition: max-height 2s ease; + -o-transition: max-height 2s ease; + transition: max-height 2s ease; + } + + .navbar-nav > li.open > ul.dropdown-menu { + display: block; + position: relative; + border: 0; + box-shadow: none; + height: auto; + max-height: 700px; + width: 100%; + padding: 10px 5%; + background: #edf2f7; + } +} + +.dropdown-menu .divider { + width: 100%; +} + +/*End New responsive nav*/ +/*COA Page*/ +.coa-hero { + background: #333 url(/themes/openstack/images/coa/COA-bkgd.jpg) no-repeat center center; + padding: 60px 0; + background-size: cover; + color: white; + margin-bottom: 40px; +} + +.coa-hero-content { + text-align: center; + background: rgba(0, 0, 0, 0.6); + padding: 20px 0; +} + +.coa-hero i.fa-graduation-cap { + padding: 10px 30px 10px 0; + border-right: 2px solid white; + margin-right: 30px; + font-size: 6em; + display: inline-block; +} + +.coa-hero h1 { + color: white; + display: inline-block; + text-align: left; + font-size: 3em; + font-weight: 400; +} + +@media (max-width: 767px) { + .coa-hero i.fa-graduation-cap { + margin-right: 0; + padding: 0; + border-right: 0; + } + + .coa-hero h1 { + display: block; + text-align: center; + margin-top: 5px; + } +} + +.coa-wrapper p { + text-align: left; + color: #2A4E68; +} + +img.coa-hero-img { + max-width: 100%; + margin: 20px 0 60px; +} + +.coa-sendgrid { + background: #edf2f7; + margin: 60px 0; + border-radius: 4px; + padding: 40px 20px; + text-align: center; +} + +.coa-sendgrid p { + color: #2A4E68; + margin-bottom: 20px; + text-align: center; +} + +.coa-sendgrid i { + color: #C8CDD3; + margin: 0 0 10px; +} + +.sendgrid-subscription-widget.coa-sendgrid input[type="email"] { + border: 2px solid #C8CDD3; + padding: 10px 20px; + border-radius: 4px; + color: #2A4E68; + font-size: 14px; + text-transform: lowercase; + font-weight: 400; + width: 100%; +} + +.sendgrid-subscription-widget.coa-sendgrid input[type="email"]:focus { + outline: none; +} + +.sendgrid-subscription-widget.coa-sendgrid label { + display: inline-block; + margin-right: 10px; + width: 40%; +} + +@media (max-width: 767px) { + .sendgrid-subscription-widget.coa-sendgrid label { + width: 70%; + } +} + +.sendgrid-subscription-widget.coa-sendgrid label>span { + display: none; +} + +.sendgrid-subscription-widget.coa-sendgrid input[type="submit"] { + background: #29abe2; + padding: 9px 20px; + border-radius: 4px; + color: white; + text-transform: uppercase; + border: 2px solid #29abe2; +} + +.sendgrid-subscription-widget.coa-sendgrid input[type="submit"]:hover { + background: #30739C; + border: 2px solid #30739C; +} + +.sendgrid-subscription-widget.coa-sendgrid input::-webkit-input-placeholder, +.sendgrid-subscription-widget.coa-sendgrid input::-webkit-input-placeholder, +.sendgrid-subscription-widget.coa-sendgrid input::-webkit-input-placeholder { + color: #C8CDD3; + font-size: 14px; + text-transform: lowercase; + font-weight: 400; +} +/*End COA Page*/ + +/*Austin Guide*/ +.guide-header { + text-align: center; +} + +.guide-header h1 { + margin-bottom: 20px; +} + +@media (max-width: 767px) { + .guide-header h1 { + margin-top: 20px; + } +} + +.guide-nav-title { + text-align: center; + margin: 35px 0 10px; + font-size: 20px; + font-weight: 400; +} + +.guide-nav { + margin: 20px 0 0; + padding: 20px 0; + background: #29abe2; +} + +.guide-nav.fixed { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1000; + margin-top: 0; +} + +.guide-nav.fixed a.guide-icons { + font-size: 12px; +} + +.guide-nav.fixed a.guide-icons i { + display: inline-block; + margin-right: 3px; + font-size: 15px; +} + +@media (max-width: 767px) { + .guide-nav.fixed a.guide-icons { + font-size: 10px; + } + + .guide-nav.fixed a.guide-icons i { + display: block; + margin-right: 0; + font-size: 17px; + } +} + +.guide-icons-wrapper { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; +} + +a.guide-icons { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + text-align: center; + text-transform: uppercase; + font-size: 12px; + font-weight: 400; + color: white; + opacity: 0.8; +} + +a.guide-icons i { + text-align: center; + display: block; + margin-bottom: 10px; + font-size: 2.2em; +} + +a.guide-icons:hover, +a.guide-icons:focus { + text-decoration: none; + opacity: 1; +} + +@media (max-width: 767px) { + a.guide-icons { + font-size: 10px; + } + + a.guide-icons i { + font-size: 30px; + margin-bottom: 10px; + } +} + +h5.section-title { + color: #2A4E68; + font-weight: 600; + text-transform: capitalize; + border-bottom: 1px solid #dae5ee; + padding-bottom: 20px; + margin: 50px 0 30px; +} + +h5.section-title span { + float: right; + text-transform: uppercase; + color: #a9a9a9; + font-weight: 700; + font-size: 0.8em; + position: relative; +} + +h5.section-title span i { + font-size: 1.3em; + margin-right: 5px; +} + +h5.section-title span:hover { + color: #29abe2; + cursor: default; +} + +h5.section-title .tooltip { + font-weight: 400; + width: auto; + white-space: normal; +} + +h5.section-title.guide { + margin: 60px 0.75% 10px; + color: #2A4E68; + text-transform: uppercase; + background: #edf2f7; + border: none; + padding: 10px 20px; + border-radius: 4px; +} + +.guide-quotes { + margin: 20px 0 30px; +} + +.guide-quotes blockquote { + font-family: Georgia, serif; + font-weight: normal; + font-size: 1.1em; + border: none; + position: relative; + text-align: center; + margin: 20px 20px 10px; +} + +.guide-quotes blockquote:before, +.guide-quotes blockquote:after { + color: #edf2f7; + font-size: 5em; + position: absolute; + top: -30px; +} + +.guide-quotes blockquote:before { + content: open-quote; + left: 0px; +} + +.guide-quotes blockquote:after { + content: close-quote; + right: 0px; +} + +.guide-quotes .guide-quote-name { + text-align: center; + font-size: 0.9em; +} + +.featured-guide-row { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + margin-bottom: 30px; +} + +.featured-guide-row .featured { + position: relative; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + margin: 10px; + border: 1px solid #D4DCE5; + border-radius: 4px; +} + +.featured-guide-row .featured .image-wrapper { + overflow: hidden; + width: 100%; + height: 180px; + background-size: cover; + background-position: center center; + background-repeat: no-repeat; + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} + +.featured-guide-row .featured .featured-content { + padding: 5px 10px 10px; +} + +.featured-guide-row .featured .featured-content h5 { + color: #2A4E68; + font-weight: 600; + margin-bottom: 10px; +} + +.featured-guide-row .featured .featured-content .address { + font-size: 0.9em; + font-weight: 600; + margin: 5px 0 10px; +} + +.featured-guide-row .featured .featured-content p { + font-size: 0.9em; +} + +.featured-guide-row .featured .featured-content a.featured-site { + color: #30739C; + padding: 3px 7px; + border-radius: 4px; + border: 1px solid #30739C; + display: inline-block; + margin-top: 5px; +} + +.featured-guide-row .featured .featured-content a.featured-site:hover { + text-decoration: none; + border-color: #29abe2; + color: #29abe2; +} + +@media (max-width: 767px) { + .featured-guide-row { + display: block; + margin-bottom: 30px; + } + + .featured-guide-row .featured { + display: block; + width: 95%; + float: left; + } + + .featured-guide-row .featured .image-wrapper { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 30%; + min-height: 100%; + height: 100%; + float: left; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + border-top-right-radius: 0; + } + + .featured-guide-row .featured .featured-content { + padding: 10px; + margin-left: 31%; + } +} + +.guide-listing { + border-bottom: 1px solid #edf2f7; + padding: 10px 0; + margin-bottom: 10px; +} + +.guide-listing .guide-name { + font-weight: 600; +} + +/*PTG*/ +/*Visa Steps*/ +.visa-steps-wrapper { + background: #edf2f7; + padding: 15px 0 0 0; + margin-bottom: 30px; + text-align: center; + position: relative; + display: block; +} + +.visa-steps-wrapper h3 { + margin: 15px 0 10px; + font-weight: 300; +} + +.visa-steps-wrapper h5 { + color: #2A4E68; + font-size: 1.1em; + margin-bottom: 20px; +} + +.visa-steps-wrapper h5 i { + margin-right: 7px; + font-size: 1.1em; +} + +.visa-steps-wrapper h5 strong { + text-transform: uppercase; +} + +.visa-steps-wrapper .visa-steps-row { + position: relative; + width: 100%; + padding: 10px 1% 0; +} + +.visa-steps-wrapper .visa-steps-row .visa-step { + width: 19%; + display: table-cell; + padding: 0 0.5%; + line-height: 1.3; +} + +.visa-steps-wrapper .visa-docs { + background: #2A4E68; + margin: 10px -1% 0 -1%; + color: white; + padding: 10px 20px; + position: relative; +} + +.visa-steps-wrapper .visa-docs:after { + bottom: 95%; + left: 86%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-color: rgba(42, 78, 104, 0); + border-bottom-color: #2A4E68; + border-width: 20px; + margin-left: -20px; +} + +.visa-steps-wrapper .visa-docs h4 { + color: white; + text-align: left; + font-weight: 300; + font-size: 1.4em; +} + +.visa-steps-wrapper .visa-docs ul { + margin: 10px 0 15px 15px; + padding: 0; + text-align: left; +} + +.visa-steps-wrapper .visa-docs ul li { + list-style: normal; + font-size: 0.9em; + font-weight: 300; +} +/*End Visa Steps*/ +/*End PTG */ + +/* OpenDev Promo */ +.opendev-earth { + max-width: 35%; + position: absolute; + left: -10%; + top: 50%; + transform: translateY(-50%); +} + +@media (max-width: 768px) { + .opendev-earth { + top: 45%; + left: 50%; + transform: translateX(-50%); + opacity: 0.5; + width: 100%; + max-width: 100%; + } +} +/* End OpenDev Promo */ diff --git a/3rd_party/static/onap-ui/assets/css/cvp-style.css b/3rd_party/static/onap-ui/assets/css/cvp-style.css new file mode 100644 index 0000000..e544458 --- /dev/null +++ b/3rd_party/static/onap-ui/assets/css/cvp-style.css @@ -0,0 +1,125 @@ +.nav>li>a { + font-size: 18px; +} + +.nav>li>a:hover, +.nav>li>a:focus, +.nav>li.active, +.nav>li.active>a { + background-color: #1080A7; + color: #E7E6E6; +} + +table { + font-size: 15px; +} + +th { + font-size: 16px; +} + +.dropdown-menu { + padding: 0px; +} + +.dropdown-menu>li>a, +.dropdown-menu>li>span { + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + white-space: nowrap; + background-color: #1C1C1C; + color: #ccc; +} + +.dropdown-menu>li>a:focus, +.dropdown-menu>li>a:hover { + font-size: 15px; + color: #1080A7; + text-decoration: none; + background-color: #1C1C1C; +} + +.pagination>.active>a, +.pagination>.active>a:focus, +.pagination>.active>a:hover, +.pagination>.active>span, +.pagination>.active>span:focus, +.pagination>.active>span:hover { + color: #fff; + background-color: #1080A7; + border-color: #1080A7; +} + + +.dropdown-menu { + border: 0px solid #ccc; +} + +.dropdown-menu>li>span>i { + cursor: pointer; +} + +.heading .logo { + display: inline-block; + padding-bottom: 30px; +} + +.heading .slogan { + display: inline-block; + margin-left: 20px; +} + +.badge-info { + color: #fff; + background-color: #1080A7; + border-color: #1080A7; +} + +.opnfv-blue { + color: #1080A7; +} + +.field { + margin-bottom: 10px; +} + +table .btn.medium { + padding: 0px 15px; +} + +.cvp-btn { + background-color: #1080A7; + font-weight: 700; + font-size: 12px; +} + +.common-main-container { + padding-left: 5%; + padding-right: 5%; +} + +a { + cursor: pointer; +} + +.btn-success-cust { +color: #fff; +background-color: #1080A7; +border-color: #1080A7; +} + +.btn-success-cust:focus, .btn-success-cust:hover { +border-color: #27CCC0; +background-color: #27CCC0; +} + +input:invalid { + border: 2px dashed red; +} + +input:valid { + border: 2px solid black; +}
\ No newline at end of file diff --git a/3rd_party/static/onap-ui/assets/css/header.css b/3rd_party/static/onap-ui/assets/css/header.css new file mode 100644 index 0000000..3f39b8a --- /dev/null +++ b/3rd_party/static/onap-ui/assets/css/header.css @@ -0,0 +1,37 @@ +.header-container-1 { + height: 80px; + background-color: #E7E6E6; +} + +.header-container-row { + margin-left: 0px; + margin-right: 0px; +} + +.header-container-2 { + height: 30px; + background-color: #E7E6E6; +} + +.header-logo { + height: 80px; + padding-left: 15px; + padding-top: 20px; +} + +.header-title { + font-size: 40px; + font-weight: bold; + color: #2C2B2F; + padding-top: 30px; +} + +.header-login { + margin-top: -20px; + margin-right: 10px; +} + +.header-splitline { + height: 5px; + background-color: #1080A7; +} diff --git a/3rd_party/static/onap-ui/assets/css/home/home.css b/3rd_party/static/onap-ui/assets/css/home/home.css new file mode 100644 index 0000000..66b3a8e --- /dev/null +++ b/3rd_party/static/onap-ui/assets/css/home/home.css @@ -0,0 +1,72 @@ +.home-container { + padding-left: 0px; + padding-right: 0px; +} + +.home-category { + border-right-style: solid; + border-left-style: solid; + padding-left: 0px; + padding-right: 0px; +} + +.home-content-title { + margin-top: 40px; + margin-bottom: 20px; + text-align: center; +} + +.home-content { + margin-top: 100px; +} + +.home-content-text { + text-align: justify; + font-size: 22px; +} + +.home-content-img { + width: 80%; +} + +#directory_inner th { + font-size: 20px; +} + +#directory_inner tbody { + font-size: 18px; +} + +#directory_inner tr { + vertical-align: middle; +} + +#directory_inner { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} + +#directory_break { + height: 1px; + display: flex; + border: 1px solid #ccc; +} + +#directory_inner > thead > tr > th { + border-bottom: 2px solid #ddd; + padding-bottom: 8px; +} + +#directory_inner > tbody > tr > td { + border-bottom: 1px solid #ddd; +} + +.company_logo { + padding: 20px 30px 20px 20px; +} + +.company_row:hover { + cursor: pointer; + text-decoration: underline; +} diff --git a/3rd_party/static/onap-ui/assets/css/index.css b/3rd_party/static/onap-ui/assets/css/index.css new file mode 100644 index 0000000..3140fce --- /dev/null +++ b/3rd_party/static/onap-ui/assets/css/index.css @@ -0,0 +1,4 @@ +.index-header { + padding-left: 0px; + padding-right: 0px; +} diff --git a/3rd_party/static/onap-ui/assets/img/icon.png b/3rd_party/static/onap-ui/assets/img/icon.png Binary files differnew file mode 100644 index 0000000..6ec8ca7 --- /dev/null +++ b/3rd_party/static/onap-ui/assets/img/icon.png diff --git a/3rd_party/static/onap-ui/assets/img/logo.png b/3rd_party/static/onap-ui/assets/img/logo.png Binary files differnew file mode 100644 index 0000000..c6f6857 --- /dev/null +++ b/3rd_party/static/onap-ui/assets/img/logo.png diff --git a/3rd_party/static/onap-ui/components/application/application.html b/3rd_party/static/onap-ui/components/application/application.html new file mode 100644 index 0000000..2238ca4 --- /dev/null +++ b/3rd_party/static/onap-ui/components/application/application.html @@ -0,0 +1,111 @@ +<div class="container-fluid common-main-container"> + <div class="results-table" style="margin-top: 30px; overflow: scroll;"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th>Create Date</th> + <th>Owner</th> + <th>ONAP version</th> + <th>Company Name</th> + <th>Company logo</th> + <th>Company Website</th> + <th>Approve date</th> + <th>Test ID</th> + <th>xNF Version</th> + <th>xNF Name</th> + <th>xNF Type</th> + <th>xNF Description</th> + <th>xNF Id</th> + <th>xNF Model Language</th> + <th>xNF Checksum (SHA256)</th> + <th>Test Period</th> + <th>Location</th> + <th>Operation</th> + </tr> + </thead> + <script type="text/ng-template" id="product.tpl.html"> + <div class="input-group"> + <span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span> + <input type="text" value="{{app.product_spec}}"> + </div> + <div class="input-group"> + <span class="input-group-addon">@</span> + <input type="text" value="{{app.product_documentation}}"> + </div> + <div class="input-group"> + <span class="input-group-addon"><i class="glyphicon glyphicon-map-marker"></i></span> + <input type="text" value="{{app.product_categories | category}}"> + </div> + </script> + <script type="text/ng-template" id="lab.tpl.html"> + <div class="input-group"> + <span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span> + <input type="text" value="{{app.lab_name}}"> + </div> + <div class="input-group"> + <span class="input-group-addon">@</span> + <input type="text" value="{{app.lab_email}}"> + </div> + <div class="input-group"> + <span class="input-group-addon"><i class="glyphicon glyphicon-map-marker"></i></span> + <input type="text" value="{{app.lab_address}}"> + </div> + <div class="input-group"> + <span class="input-group-addon"><i class="glyphicon glyphicon-phone"></i></span> + <input type="text" value="{{app.lab_phone}}"> + </div> + </script> + <tbody style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"> + <tr ng-if="results.status != 'review'" ng-repeat="app in ctrl.applications"> + <td>{{ app.creation_date | limitTo: 10 }}</td> + <td>{{ app.owner }}</td> + <td>{{ app.onap_version }}</td> + <td>{{ app.company_name }}</td> + <td>{{ app.company_logo }}</td> + <td>{{ app.company_website }}</td> + <td>{{ app.approve_date | limitTo: 10 }}</td> + <td>{{ app.test_id }}</td> + <td>{{ app.xnf_version }}</td> + <td>{{ app.xnf_name }}</td> + <td>{{ app.xnf_type }}</td> + <td>{{ app.xnf_description }}</td> + <td>{{ app.xnfd_id }}</td> + <td>{{ app.xnfd_model_lang }}</td> + <td>{{ app.xnf_checksum }}</td> + <td>{{ app.xnf_test_period }}</td> + <td> + <span popover-enable="app.lab_location != 'internal'" uib-popover-template="ctrl.lab_tpl" + popover-title="Lab Info" popover-placement="top" + popover-trigger="mouseenter">{{ app.lab_location | labLocation}}</span> + <i ng-if="app.lab_location != 'internal'" class="glyphicon glyphicon-info-sign opnfv-blue"></i> + </td> + <td> + <a ng-click="ctrl.toggleApproveApp(app._id, 'true')" class="badge badge-info" + ng-if="app.approved == 'false'" + data-toggle="tooltip" title="Approve Application"> + <i class="glyphicon glyphicon-ok" ></i> + </a> + <a ng-click="ctrl.deleteApp(app._id)" class="badge badge-info" + data-toggle="tooltip" title="Delete Application"> + <i class="glyphicon glyphicon-remove" ></i> + </a> + </td> + </tr> + </tbody> + </table> + + <div class="pages"> + <uib-pagination + total-items="ctrl.totalItems" + ng-model="ctrl.currentPage" + items-per-page="ctrl.itemsPerPage" + max-size="ctrl.maxSize" + class="pagination-sm" + boundary-links="true" + rotate="false" + num-pages="ctrl.numPages" + ng-change="ctrl.updatePage()"> + </uib-pagination> + </div> + </div> +</div> diff --git a/3rd_party/static/onap-ui/components/application/applicationController.js b/3rd_party/static/onap-ui/components/application/applicationController.js new file mode 100644 index 0000000..094ffdc --- /dev/null +++ b/3rd_party/static/onap-ui/components/application/applicationController.js @@ -0,0 +1,110 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('ApplicationController', ApplicationController); + + ApplicationController.$inject = [ + '$http', '$stateParams', '$window', '$sce', + '$uibModal', 'testapiApiUrl', 'raiseAlert', 'ngDialog', '$scope' + ]; + + /** + */ + function ApplicationController($http, $stateParams, $window, $sce, + $uibModal, testapiApiUrl, raiseAlert, ngDialog, $scope) { + + var ctrl = this; + + function init() { + ctrl.applications = []; + + ctrl.totalItems = null; + ctrl.currentPage = 1; + ctrl.itemsPerPage = 5; + ctrl.numPages = null; + + ctrl.lab_tpl = "lab.tpl.html"; + ctrl.product_tpl = "product.tpl.html"; + + getApplication(); + } + + ctrl.updatePage = function() { + getApplication(); + } + + ctrl.deleteApp = function(id) { + var resp = confirm('Are you sure you want to delete this application?'); + if (!resp) + return; + + var delUrl = testapiApiUrl + "/cvp/applications/" + id; + $http.delete(delUrl) + .then(function(ret) { + if (ret.data.code && ret.data.code != 0) { + alert(ret.data.msg); + return; + } + getApplication(); + }); + } + + ctrl.toggleApproveApp = function(id, approved) { + if (approved === 'true') { + var text = 'Are you sure you want to approve this application?'; + } else { + var text = 'Are you sure you want to remove approval of this application?'; + } + + var resp = confirm(text); + if (!resp) + return; + + var updateUrl = testapiApiUrl + "/cvp/applications/" + id; + var data = {}; + data['item'] = 'approved'; + data['approved'] = approved; + + $http.put(updateUrl, JSON.stringify(data), { + transformRequest: angular.identity, + headers: {'Content-Type': 'application/json'}}).then(function(ret) { + if (ret.data.code && ret.data.code != 0) { + alert(ret.data.msg); + return; + } + getApplication(); + }, function(error) { + alert('Error when update data'); + }); + } + + function getApplication() { + $http.get(testapiApiUrl + "/onap/cvp/applications?page=" + ctrl.currentPage + "&signed&per_page=" + ctrl.itemsPerPage).then(function(response) { + ctrl.applications = response.data.applications; + ctrl.totalItems = response.data.pagination.total_pages * ctrl.itemsPerPage; + ctrl.currentPage = response.data.pagination.current_page; + ctrl.numPages = response.data.pagination.total_pages; + }, function(error) { + /* do nothing */ + }); + } + + init(); + } +})(); diff --git a/3rd_party/static/onap-ui/components/auth-failure/authFailureController.js b/3rd_party/static/onap-ui/components/auth-failure/authFailureController.js new file mode 100644 index 0000000..29d1d70 --- /dev/null +++ b/3rd_party/static/onap-ui/components/auth-failure/authFailureController.js @@ -0,0 +1,33 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('AuthFailureController', AuthFailureController); + + AuthFailureController.$inject = ['$location', '$state', 'raiseAlert']; + /** + * TestAPI Auth Failure Controller + * This controller handles messages from TestAPI API if user auth fails. + */ + function AuthFailureController($location, $state, raiseAlert) { + var ctrl = this; + ctrl.message = $location.search().message; + raiseAlert('danger', 'Authentication Failure:', ctrl.message); + $state.go('home'); + } +})(); diff --git a/3rd_party/static/onap-ui/components/directory/directory.html b/3rd_party/static/onap-ui/components/directory/directory.html new file mode 100644 index 0000000..4a04bd7 --- /dev/null +++ b/3rd_party/static/onap-ui/components/directory/directory.html @@ -0,0 +1,34 @@ +<div class="container-fluid common-main-container"> + <h3>ONAP Verified Product Directory</h3> + <div> + <h4>Compliance Marks Granted to {{ctrl.companyID}}</h4> + <img src="api/v1/cvp/applications/getlogo/{{ctrl.company_logo}}" alt="Company Logo"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th>Product Name</th> + <th>Description</th> + <th>Model Language</th> + <th>Artifact Checksum (SHA256)</th> + <th>Product Version</th> + <th>Version</th> + <th>Date</th> + <th>Product Info</th> + </tr> + </thead> + <tbody class="directory_inner" style="overflow: hidden; text-overflow: ellipsis;"> + <tr style="vertical-align: center;" ng-repeat="prod in ctrl.directory" + ng-if="prod.company_name==ctrl.companyID && prod.approved=='true'"> + <td style="width: 250px;">{{ prod.xnf_name }}</td> + <td style="width: 350px;">{{ prod.xnf_description }}</td> + <td style="width: 350px;">{{ prod.xnfd_model_lang }}</td> + <td style="width: 350px;">{{ prod.xnf_checksum }}</td> + <td style="width: 150px;">{{ prod.xnf_version }}</td> + <td style="width: 150px;">{{ prod.onap_version }}</td> + <td style="width: 150px;">{{ prod.approve_date | limitTo: 10 }}</td> + <td style="width: 150px;"><a href="{{ prod.company_website }}" target="_blank" rel="noopener">{{ prod.company_website }}</a></td> + </tr> + </tbody> + </table> + </div> +</div> diff --git a/3rd_party/static/onap-ui/components/directory/directoryController.js b/3rd_party/static/onap-ui/components/directory/directoryController.js new file mode 100644 index 0000000..a4fc6f3 --- /dev/null +++ b/3rd_party/static/onap-ui/components/directory/directoryController.js @@ -0,0 +1,45 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('DirectoryController', DirectoryController); + + DirectoryController.$inject = ['$http', '$stateParams', + 'testapiApiUrl' + ]; + + /** + * This controller handles the directory page + */ + function DirectoryController($http, $stateParams, testapiApiUrl) { + var ctrl = this; + + ctrl.companyID = $stateParams.companyID; + ctrl.company_logo = $stateParams.logo; + getDirectory(); + + function getDirectory() { + $http.get(testapiApiUrl + "/onap/cvp/applications").then(function(response) { + ctrl.directory = response.data.applications; + }, function(error) { + /* do nothing */ + }); + } + } + +})(); diff --git a/3rd_party/static/onap-ui/components/home/home.html b/3rd_party/static/onap-ui/components/home/home.html new file mode 100644 index 0000000..4db08b0 --- /dev/null +++ b/3rd_party/static/onap-ui/components/home/home.html @@ -0,0 +1,170 @@ +<div class="container-fluid"> + <div class="row"> + <div class="col-md-2 home-category" ng-style="{'height': ctrl.height}"> + <div class="panel-group" id="accordion"> + <div class="panel panel-default"> + <div class="panel-heading"> + <div class="panel-title"> + <a data-toggle="collapse" data-parent="#accordion" href="#collapseOne"> + Governance & Workflow + </a> + </div> + </div> + <div id="collapseOne" class="panel-collapse collapse"> + <div class="panel-body"> + <div> + <a href="https://www.opnfv.org/verified" + target="_blank" rel="noopener">Overview + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + <div> + <a href="https://www.opnfv.org/wp-content/uploads/sites/12/2018/09/LFN_CVP_Guidelines-1.0.0.pdf" + target="_blank" rel="noopener">Governance Guidelines + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + <div> + <a href="https://www.opnfv.org/wp-content/uploads/sites/12/2018/09/OVP-Terms-and-Conditions-092418.pdf" + target="_blank" rel="noopener">Terms & Conditions + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + <div> + <a href="http://docs.opnfv.org/en/stable-fraser/submodules/dovetail/docs/testing/user/certificationworkflow/index.html" + target="_blank" rel="noopener">Process Workflow + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + <div> + <a href="https://na3.docusign.net/Member/PowerFormSigning.aspx?PowerFormId=dc24bf38-ea41-40d4-9e58-9babc6eec778" + target="_blank" rel="noopener">Participation Form + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + <div> + <a href="https://wiki.onap.org/display/DW/VNF+Badging" + target="_blank" rel="noopener">ONAP Verified Brand Guidelines + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + </div> + </div> + </div> + <div class="panel panel-default" style="margin-top: 0px;"> + <div class="panel-heading"> + <div class="panel-title"> + <a data-toggle="collapse" data-parent="#accordion" href="#collapseTwo"> + Release 2018.11 + </a> + </div> + </div> + <div id="collapseTwo" class="panel-collapse collapse"> + <div class="panel-body"> + <div> + <a href="http://docs.opnfv.org/en/stable-fraser/submodules/dovetail/docs/testing/user/userguide/testing_guide.html" + target="_blank" rel="noopener">ONAPVP / Dovetail User Guide + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + <div> + <a href="http://docs.opnfv.org/en/stable-fraser/submodules/dovetail/docs/testing/user/userguide/cli_reference.html" + target="_blank" rel="noopener">Dovetail CLI + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + <div> + <a href="https://wiki.onap.org/display/DW/ONAP+Modeling+specification" + target="_blank" rel="noopener">Test Specifications + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + <div> + <a href="https://wiki.onap.org/display/DW/Release+Calendar" + target="_blank" rel="noopener">Release Notes + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + <div> + <a href="http://docs.opnfv.org/en/stable-fraser/submodules/dovetail/docs/testing/user/reviewerguide/index.html" + target="_blank" rel="noopener">Reviewer Guide + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + <div> + <a href="http://docs.opnfv.org/en/stable-fraser/submodules/dovetail/docs/testing/user/ovpaddendum/index.html" + target="_blank" rel="noopener">Guidelines Addendum + <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> + </a> + </div> + </div> + </div> + </div> + </div> + </div> + + <div class="col-md-10"> + <div class="common-main-container"> + <div class="home-content-title"> + <h2>ONAP Verified Program verifies products and services with the "ONAP Verified" mark.</h2> + </div> + <div class="home-content"> + <div class="col-md-2"> + <img class="home-content-img" src="onap-ui/assets/img/icon.png" alt="ONAP"> + </div> + <div class="col-md-10"> + <p class="home-content-text" style="padding-bottom: 40px;"> + The ONAP Verified Program demonstrates the readiness and availability of commercial VNF/PNF + products and services by implementing Specification defined by Compliance Verification + Committee(CVC). This open source community-led initiative allows vendors and service + providers to establish baseline conformance and interoperability while retaining distinct + and value-added innovations across features and capabilities and requires verified products + to complete additional functional, high-availability tests. View the directory of verified + products and services below and navigate through the links in the left-hand menu to learn + more and get started. You will find step-by-step instructions as well as a participation form. + Use this portal to upload your test results when ready. Please send any questions to + <a href="mailto:compliance@lists.lfnetworking.org">compliance@lists.lfnetworking.org</a><sup> + <span class="glyphicon glyphicon-envelope" style="font-size: 60%;" aria-hidden="true"></span></sup>. + </p> + </div> + </div> + <div id="directory_break"> + </div> + <div class="home-content-title"> + <h1>ONAP Verified Products Directory</h1> + Click on rows for more product verification details per company. + </div> + <div class="directory_main"> + <table id="directory_inner" class=""> + <thead> + <tr> + <th>Company Name & Logo</th> + <th>Test Agency</th> + <th>Product Name</th> + <th>Product Version</th> + <th>Modeling Language</th> + <th>Type</th> + <th>Version</th> + </tr> + </thead> + <tbody style="overflow: hidden; text-overflow: ellipsis;"> + <tr class="company_row" ng-click="ctrl.getCompany(app)" + ng-repeat="app in ctrl.applications | filter:{approved:true} | orderBy : '-approve_date'"> + <td style="width: 400px;"> + <img class="company_logo" ng-src="api/v1/cvp/applications/getlogo/{{app.company_logo}}" alt="{{app.company_logo}}"> + {{ app.company_name }} + </td> + <td style="width: 300px;">{{ app.lab_location }}</td> + <td style="width: 300px;">{{ app.xnf_name }}</td> + <td style="width: 150px;">{{ app.xnf_version }}</td> + <td style="width: 150px;">{{ app.xnfd_model_lang }}</td> + <td style="width: 150px;">N/A</td> + <td style="width: 150px;">{{ app.onap_version}}</td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + </div> +</div> diff --git a/3rd_party/static/onap-ui/components/home/homeController.js b/3rd_party/static/onap-ui/components/home/homeController.js new file mode 100644 index 0000000..678dd2a --- /dev/null +++ b/3rd_party/static/onap-ui/components/home/homeController.js @@ -0,0 +1,58 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('HomeController', HomeController); + + HomeController.$inject = [ + '$http', '$rootScope', '$state', 'testapiApiUrl' + ]; + + /** + * TestAPI Results Controller + * This controller is for the '/results' page where a user can browse + * a listing of community uploaded results. + */ + function HomeController($http, $rootScope, $state, testapiApiUrl) { + var ctrl = this; + getApplication(); + + ctrl.height = $(document).height() + 500; + + ctrl.gotoApplication = function() { + if ($rootScope.auth.isAuthenticated) { + $state.go('application'); + } else { + $rootScope.auth.doSignIn('cas'); + } + } + + function getApplication() { + $http.get(testapiApiUrl + "/onap/cvp/applications").then(function(response) { + ctrl.applications = response.data.applications; + }, function(error) { + /* do nothing */ + }); + } + + ctrl.getCompany = function(row) { + $state.go('directory', {'companyID': row.company_name, 'logo': row.company_logo}); + } + + } +})(); diff --git a/3rd_party/static/onap-ui/components/logout/logout.html b/3rd_party/static/onap-ui/components/logout/logout.html new file mode 100644 index 0000000..38a5c36 --- /dev/null +++ b/3rd_party/static/onap-ui/components/logout/logout.html @@ -0,0 +1 @@ +<div cg-busy="{promise:ctrl.redirectWait,message:'Logging you out...'}"></div> diff --git a/3rd_party/static/onap-ui/components/logout/logoutController.js b/3rd_party/static/onap-ui/components/logout/logoutController.js new file mode 100644 index 0000000..1b6d78c --- /dev/null +++ b/3rd_party/static/onap-ui/components/logout/logoutController.js @@ -0,0 +1,44 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('LogoutController', LogoutController); + + LogoutController.$inject = [ + '$location', '$window', '$timeout' + ]; + + /** + * TestAPI Logout Controller + * This controller handles logging out. In order to fully logout, the + * openstackid_session cookie must also be removed. The way to do that + * is to have the user's browser make a request to the openstackid logout + * page. We do this by placing the logout link as the src for an html + * image. After some time, the user is redirected home. + */ + function LogoutController($location, $window, $timeout) { + var ctrl = this; + + ctrl.openid_logout_url = $location.search().openid_logout; + var img = new Image(0, 0); + img.src = ctrl.openid_logout_url; + ctrl.redirectWait = $timeout(function() { + $window.location.href = '/'; + }, 500); + } +})(); diff --git a/3rd_party/static/onap-ui/components/profile/profile.html b/3rd_party/static/onap-ui/components/profile/profile.html new file mode 100644 index 0000000..cb73335 --- /dev/null +++ b/3rd_party/static/onap-ui/components/profile/profile.html @@ -0,0 +1,88 @@ +<div class="container-fluid common-main-container"> + <h3>User profile</h3> + <div cg-busy="{promise:ctrl.authRequest,message:'Loading'}"></div> + <div> + <table class="table table-striped table-hover"> + <tbody> + <tr> + <td>User name</td> + <td>{{auth.currentUser.fullname}}</td> + </tr> + <tr> + <td>User OpenId</td> + <td>{{auth.currentUser.openid}}</td> + </tr> + <tr> + <td>Email</td> + <td>{{auth.currentUser.email}}</td> + </tr> + <tr> + <td>Role</td> + <td>{{auth.currentUser.role}}</td> + </tr> + </tbody> + </table> + </div> + <br> + <br> + <h3>Organization Details</h3> + <table class="table table-striped table-hover"> + <tbody> + <tr> + <td>Company Name</td> + <td> + <div class="popover-wrapper"> + <a editable-theme="bs3" onbeforesave="ctrl.changeProfileDetails(ctrl.profile, 'companyName', $data)" + editable-text="ctrl.profile.companyName">{{ ctrl.profile.companyName || "None" }}</a> + </div> + </td> + </tr> + <tr> + <td>Company website</td> + <td> + <div class="popover-wrapper"> + <a editable-theme="bs3" onbeforesave="ctrl.changeProfileDetails(ctrl.profile, 'companyWebsite', $data)" + editable-url="ctrl.profile.companyWebsite">{{ ctrl.profile.companyWebsite || "None" }}</a> + </div> + </td> + </tr> + <tr> + <td>Primary Contact Name</td> + <td> + <div class="popover-wrapper"> + <a editable-theme="bs3" onbeforesave="ctrl.changeProfileDetails(ctrl.profile, 'primaryContactName', $data)" + editable-text="ctrl.profile.primaryContactName">{{ ctrl.profile.primaryContactName || "None" }}</a> + </div> + </td> + </tr> + <tr> + <td>Primary Contact Business email</td> + <td> + <div class="popover-wrapper"> + <a editable-theme="bs3" onbeforesave="ctrl.changeProfileDetails(ctrl.profile, 'primaryBusinessEmail', $data)" + editable-email="ctrl.profile.primaryBusinessEmail">{{ ctrl.profile.primaryBusinessEmail || "None" }}</a> + </div> + </td> + </tr> + <tr> + <td>Primary Contact Postal Address</td> + <td> + <div class="popover-wrapper"> + <a editable-theme="bs3" onbeforesave="ctrl.changeProfileDetails(ctrl.profile, 'primaryPostalAddress', $data)" + editable-text="ctrl.profile.primaryPostalAddress">{{ ctrl.profile.primaryPostalAddress || "None" }}</a> + </div> + </td> + </tr> + <tr> + <td>Primary Contact Phone Number</td> + <td> + <div class="popover-wrapper"> + <a editable-theme="bs3" onbeforesave="ctrl.changeProfileDetails(ctrl.profile, 'primaryPhoneNumber', $data)" + editable-tel="ctrl.profile.primaryPhoneNumber">{{ ctrl.profile.primaryPhoneNumber || "None" }}</a> + </div> + </td> + </tr> + </tbody> + </table> + </div> +</div> diff --git a/3rd_party/static/onap-ui/components/profile/profileController.js b/3rd_party/static/onap-ui/components/profile/profileController.js new file mode 100644 index 0000000..1b84d76 --- /dev/null +++ b/3rd_party/static/onap-ui/components/profile/profileController.js @@ -0,0 +1,68 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('ProfileController', ProfileController); + + ProfileController.$inject = [ + '$scope', '$state', '$http', 'testapiApiUrl' + ]; + + /** + * TestAPI Profile Controller + * This controller handles user's profile page, where a user can view + * account-specific information. + */ + function ProfileController($scope, $state, $http, testapiApiUrl) { + + var ctrl = this; + + ctrl.changeProfileDetails = changeProfileDetails; + + // Must be authenticated to view this page. + if (!$scope.auth.isAuthenticated) { + $state.go('home'); + } + + ctrl.authRequest = $scope.auth.doSignCheck(); + ctrl.profile = $scope.auth.currentUser; + + function changeProfileDetails(profile, key, newValue){ + if (profile[key] === newValue) { + return; + } + var updateUrl = testapiApiUrl + "/profile"; + + var data = {}; + data[key] = newValue; + + $http.put(updateUrl, JSON.stringify(data), { + transformRequest: angular.identity, + headers: {'Content-Type': 'application/json'}}).then(function(ret) { + if (ret.data.code && ret.data.code != 0) { + alert(ret.data.msg); + } else { + profile[key] = newValue; + } + }, function(error) { + alert("Error when update data"); + }); + } + } +})(); diff --git a/3rd_party/static/onap-ui/components/results-report/data/2019.04/heat-testcases.json b/3rd_party/static/onap-ui/components/results-report/data/2019.04/heat-testcases.json new file mode 100644 index 0000000..1f584f4 --- /dev/null +++ b/3rd_party/static/onap-ui/components/results-report/data/2019.04/heat-testcases.json @@ -0,0 +1,13 @@ +{ + "mandatory": { + "onap-vvp.validate.heat": { + "cases": [ + "onap-vvp.validate.heat" + ], + "total": 1 + } + }, + "optional": { + + } +} diff --git a/3rd_party/static/onap-ui/components/results-report/data/2019.04/tosca-testcases.json b/3rd_party/static/onap-ui/components/results-report/data/2019.04/tosca-testcases.json new file mode 100644 index 0000000..de01e83 --- /dev/null +++ b/3rd_party/static/onap-ui/components/results-report/data/2019.04/tosca-testcases.json @@ -0,0 +1,13 @@ +{ + "mandatory": { + "onap-vtp.validate.csar": { + "cases": [ + "onap-vtp.validate.csar" + ], + "total": 1 + } + }, + "optional": { + + } +} diff --git a/3rd_party/static/onap-ui/components/results-report/partials/reportDetails.html b/3rd_party/static/onap-ui/components/results-report/partials/reportDetails.html new file mode 100644 index 0000000..3f3e9c9 --- /dev/null +++ b/3rd_party/static/onap-ui/components/results-report/partials/reportDetails.html @@ -0,0 +1,60 @@ +<!-- +HTML for each accordion group that separates the status types on the results +report page. +--> + +Test Filters:<br> +<div class="btn-toolbar" role="toolbar"> + <div class="btn-group button-margin" data-toggle="buttons"> + <label class="btn btn-default" ng-click="ctrl.changeStatus('total')" + ng-class="{'active': ctrl.testStatus === 'total'}"> + <input type="radio" ng-model="ctrl.testStatus" value="total" name="total"> + <span class="text-primary">All</span> + </label> + <label class="btn btn-default" ng-click="ctrl.changeStatus('passed')" + ng-class="{'active': ctrl.testStatus === 'passed'}"> + <input type="radio" ng-model="ctrl.testStatus" value="passed" name="passed"> + <span class="text-success">Passed</span> + </label> + <label class="btn btn-default" ng-click="ctrl.changeStatus('not passed')" + ng-class="{'active': ctrl.testStatus === 'not passed'}"> + <input type="radio" ng-model="ctrl.testStatus" value="not passed" name="not passed"> + <span class="text-danger">Not Passed</span> + </label> + </div> + <div class="btn-group button-margin" style="float: right;"> + <button type="button" class="btn btn-default" ng-click="ctrl.openAll()">Expand</button> + <button type="button" class="btn btn-default" ng-click="ctrl.folderAll()">Collapse</button> + </div> +</div> + +<uib-accordion-group ng-repeat="(type,data) in ctrl.data" is-open="isOpen"> + <uib-accordion-heading> + {{ type }}: {{ ctrl.statistics[type].total }} tests + <i class="pull-right glyphicon" + ng-class="{'glyphicon-chevron-down': isOpen, 'glyphicon-chevron-right': !isOpen}"></i> + </uib-accordion-heading> + <ol class="capabilities"> + <li ng-repeat="(area, value) in data" + ng-show="(ctrl.testStatus == 'passed' && value.pass != 0) || (ctrl.testStatus == 'not passed' && value.fail != 0) || ctrl.testStatus == 'total'"> + <a ng-click="value.folder = !value.folder"> + {{ area }} + <span ng-if="ctrl.testStatus == 'total'" + ng-class="{'text-success': value.total == value.pass, 'text-warning': (value.pass < value.total && value.pass > 0), 'text-danger': value.pass == 0}"> + [{{ value.pass }}/{{ value.total }}] + </span> + <span ng-if="ctrl.testStatus == 'passed'" class="text-success">[{{ value.pass }}]</span> + <span ng-if="ctrl.testStatus == 'not passed'" class="text-danger">[{{ value.fail }}]</span> + </a> + <a uib-tooltip="view log" ng-click="ctrl.gotoResultLog(area)"><span class="glyphicon glyphicon-cog"></span></a> + <ul class="list-unstyled" uib-collapse="value.folder"> + <li ng-repeat="case in value.cases" + ng-if="(ctrl.testStatus=='passed' && ctrl.case_list.indexOf(case) > -1) || (ctrl.testStatus=='not passed' && ctrl.case_list.indexOf(case) == -1) || ctrl.testStatus=='total'"> + <span ng-class="{'glyphicon glyphicon-ok text-success':ctrl.case_list.indexOf(case) > -1, 'glyphicon glyphicon-remove text-warning':ctrl.case_list.indexOf(case) == -1}" + aria-hidden="true"></span> + <a ng-click="ctrl.gotoDoc(case)">{{ case }}</a> + </li> + </ul> + </li> + </ol> +</uib-accordion-group> diff --git a/3rd_party/static/onap-ui/components/results-report/resultsReport.html b/3rd_party/static/onap-ui/components/results-report/resultsReport.html new file mode 100644 index 0000000..38e602d --- /dev/null +++ b/3rd_party/static/onap-ui/components/results-report/resultsReport.html @@ -0,0 +1,33 @@ +<div class="container-fluid common-main-container"> + <h3>Test Run Results</h3> + + <div ng-show="ctrl.testId" class="container-fluid"> + <div class="row"> + <div class="pull-left"> + <div class="test-report"> + <strong>ONAP Version:</strong> {{ctrl.version}}<br> + <strong>Test ID:</strong> {{ctrl.testId}}<br> + </div> + </div> + </div> + </div> + + <strong>Total: {{ctrl.statistics.total}}, Pass: {{ ctrl.statistics.pass}}, Rate: {{ ctrl.statistics.pass / ctrl.statistics.total * 100 | number:2 }}%</strong><br> + <strong>Mandatory Total: {{ctrl.statistics.mandatory.total}}, Pass: {{ ctrl.statistics.mandatory.pass }}, Rate: {{ ctrl.statistics.mandatory.pass / ctrl.statistics.mandatory.total * 100 | number:2 }}%</strong><br> + <strong>Optional Total: {{ctrl.statistics.optional.total}}, Pass: {{ ctrl.statistics.optional.pass }}, Rate: {{ ctrl.statistics.optional.pass / ctrl.statistics.optional.total * 100 | number:2 }}%</strong><br> + <hr> + <strong>{{ ctrl.validation }}</strong><br> + + <div> + <hr> + <h4>Test Result Overview</h4> + <uib-accordion close-others=false> + <!-- The ng-repeat is used to pass in a local variable to the template. --> + <ng-include + src="ctrl.detailsTemplate" + onload="isOpen = true"> + </ng-include> + <br> + </uib-accordion> + </div> +</div> diff --git a/3rd_party/static/onap-ui/components/results-report/resultsReportController.js b/3rd_party/static/onap-ui/components/results-report/resultsReportController.js new file mode 100644 index 0000000..09601ad --- /dev/null +++ b/3rd_party/static/onap-ui/components/results-report/resultsReportController.js @@ -0,0 +1,224 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('ResultsReportController', ResultsReportController); + + ResultsReportController.$inject = [ + '$scope', '$http', '$stateParams', '$window', + 'testapiApiUrl' + ]; + + /** + * TestAPI Results Report Controller + * This controller is for the '/results/<test run ID>' page where a user can + * view details for a specific test run. + */ + function ResultsReportController($scope, $http, $stateParams, $window, + testapiApiUrl) { + + var ctrl = this; + + ctrl.testStatus = 'total'; + ctrl.case_list = []; + ctrl.data = {}; + ctrl.statistics = { + 'total': 0, 'pass': 0, 'fail': 0, + 'mandatory': {'total': 0, 'pass': 0, 'fail': 0, 'area': 0}, + 'optional': {'total': 0, 'pass': 0, 'fail': 0, 'area': 0} + }; + + ctrl.gotoDoc = gotoDoc; + ctrl.openAll = openAll; + ctrl.folderAll = folderAll; + ctrl.gotoResultLog = gotoResultLog; + ctrl.changeStatus = changeStatus; + + /** The testID extracted from the URL route. */ + ctrl.testId = $stateParams.testID; + ctrl.innerId = $stateParams.innerID; + ctrl.validation = ''; + ctrl.vnf_type = ''; + ctrl.vnf_checksum = ''; + ctrl.version = ''; + + /** The HTML template that all accordian groups will use. */ + ctrl.detailsTemplate = 'onap-ui/components/results-report/partials/' + + 'reportDetails.html'; + + $scope.load_finish = false; + + function changeStatus(value) { + ctrl.testStatus = value; + } + + function extend(case_list) { + angular.forEach(case_list, function(ele) { + ctrl.case_list.push(ele); + }); + } + + function strip(word) { + return word.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + } + + function gotoResultLog(case_name) { + var case_area = case_name.split(".")[0]; + var log_url = "/logs/" + ctrl.testId + "/results/"; + log_url += case_area + "_logs/" + case_name + ".out"; + var is_reachable = false; + + $.ajax({ + url: log_url, + async: false, + success: function (response) { + is_reachable = true; + }, + error: function (response) { + alert("Log file could not be found. Please confirm this case has been executed successfully."); + } + }); + + if (is_reachable == true) { + window.open(log_url); + } + } + + $scope.$watch('load_finish', function() { + if ($scope.load_finish == true) { + var case_url = 'onap-ui/components/results-report/data/' + ctrl.version + '/' + ctrl.vnf_type + '-testcases.json' + $http.get(case_url).then(function(response) { + ctrl.data = response.data; + + angular.forEach(ctrl.data.mandatory, function(value, name) { + ctrl.data.mandatory[name].folder = true; + ctrl.data.mandatory[name].pass = 0; + ctrl.data.mandatory[name].fail = 0; + angular.forEach(value.cases, function(sub_case) { + ctrl.statistics.total += 1; + ctrl.statistics.mandatory.total += 1; + if (ctrl.case_list.indexOf(sub_case) > -1) { + ctrl.data.mandatory[name].pass += 1; + ctrl.statistics.mandatory.pass += 1; + ctrl.statistics.pass += 1; + } else { + ctrl.data.mandatory[name].fail += 1; + ctrl.statistics.mandatory.fail += 1; + ctrl.statistics.fail += 1; + } + }); + }); + + angular.forEach(ctrl.data.optional, function(value, name) { + ctrl.data.optional[name].folder = true; + ctrl.data.optional[name].pass = 0; + ctrl.data.optional[name].fail = 0; + angular.forEach(value.cases, function(sub_case) { + ctrl.statistics.total += 1; + ctrl.statistics.optional.total += 1; + if (ctrl.case_list.indexOf(sub_case) > -1) { + ctrl.data.optional[name].pass += 1; + ctrl.statistics.optional.pass += 1; + ctrl.statistics.pass += 1; + } else { + ctrl.data.optional[name].fail += 1; + ctrl.statistics.optional.fail += 1; + ctrl.statistics.fail += 1; + } + + }); + }); + + ctrl.statistics.mandatory.area = Object.keys(ctrl.data.mandatory).length; + ctrl.statistics.optional.area = Object.keys(ctrl.data.optional).length; + }, function(error) { + alert('error to get test case info'); + }); + } + }); + + function generate_format_data() { + var test_url = testapiApiUrl + '/onap/tests/' + ctrl.innerId; + $http.get(test_url).then(function(test_resp) { + ctrl.validation = test_resp.data.validation; + + angular.forEach(test_resp.data.results, function(result, index) { + var result_url = testapiApiUrl + '/results/' + result; + $http.get(result_url).then(function(result_resp) { + + ctrl.version = result_resp.data.version; + ctrl.vnf_type = result_resp.data.vnf_type; + + angular.forEach(result_resp.data.testcases_list, function(testcase, index) { + var sub_case_list = get_sub_case_list_2019_04(testcase); + extend(sub_case_list); + }); + + if (index == test_resp.data.results.length - 1) { + $scope.load_finish = true; + } + }, function(result_error) { + /* do nothing */ + }); + }); + + }, function(test_error) { + alert('Error when get test record'); + }); + } + + function get_sub_case_list_2019_04(result) { + var case_list = []; + if (result.sub_testcase.length == 0 && result.result == "PASS") { + case_list.push(result.name); + } else { + angular.forEach(result.sub_testcase, function(subtest, index) { + if (subtest.result == "PASS") { + case_list.push(subtest.name) + } + }); + } + return case_list; + } + + function gotoDoc(sub_case) { + /* not implemented */ + } + + function openAll() { + angular.forEach(ctrl.data.mandatory, function(ele, id) { + ele.folder = false; + }); + angular.forEach(ctrl.data.optional, function(ele, id) { + ele.folder = false; + }); + } + + function folderAll() { + angular.forEach(ctrl.data.mandatory, function(ele, id) { + ele.folder = true; + }); + angular.forEach(ctrl.data.optional, function(ele, id) { + ele.folder = true; + }); + } + + generate_format_data(); + } + +})(); diff --git a/3rd_party/static/onap-ui/components/results/modal/applicationModal.html b/3rd_party/static/onap-ui/components/results/modal/applicationModal.html new file mode 100644 index 0000000..0ca4b84 --- /dev/null +++ b/3rd_party/static/onap-ui/components/results/modal/applicationModal.html @@ -0,0 +1,158 @@ +<div class="container-fluid common-main-container"> + <div class="top-site-banner"> + <div class="container"> + <p class="p1">Application Details</p> + </div> + </div> + + <div class="row" style="margin-top: 20px;"> + <div class="col-lg-12 container"> + <p class="message" style="display: none;"></p> + <fieldset> + <div class="field text col-md-4"> + <label class="left">Company Name</label> + <i uib-tooltip="Company Name" class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <input type="text" class="text form-control" ng-model="ctrl.company_name" + ng-init="ctrl.company_name=auth.currentUser.companyName" required> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">Company Logo</label> + <i uib-tooltip="Required dimensions (Max Values): {width: 165pixels, height: 40pixels}" class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <input class="form-control btn btn-success-cust cvp-btn medium accent-color regular-button" + modal-file-model="logoFile" type="file" style="padding: 0;" required> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">Company Website</label> + <i uib-tooltip="Company Website" class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <input type="url" class="text form-control" ng-model="ctrl.company_website" + ng-init="ctrl.company_website=auth.currentUser.companyWebsite" required> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">Primary Contact Name</label> + <i uib-tooltip="Primary Contact name (optional)" + class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <input type="text" class="text form-control" ng-model="ctrl.primary_contact_name" + ng-init="ctrl.primary_contact_name=auth.currentUser.primaryContactName" required> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">Primary Contact Number</label> + <i uib-tooltip="Primary Contact phone number(optional)" + class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <input type="tel" class="text form-control" ng-model="ctrl.primary_phone_number" + ng-init="ctrl.primary_phone_number=auth.currentUser.primaryPhoneNumber" required> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">Primary Contact Email</label> + <i uib-tooltip="Primary Contact email (optional)" + class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <input type="email" class="text form-control" ng-model="ctrl.primary_business_email" + ng-init="ctrl.primary_business_email=auth.currentUser.primaryBusinessEmail" required> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">Test Agency</label> + <i uib-tooltip="Location" class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <select class="form-control" ng-model="ctrl.lab_location" ng-init="ctrl.lab_location='internal'" required> + <option value="internal">1st Party - Submitter</option> + <option value="third">3rd Party - External Lab</option> + </select> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">xNF Version</label> + <i uib-tooltip="xNF Version" + class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <input type="text" class="text form-control" ng-model="ctrl.xnf_version" required> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">xNF Name</label> + <i uib-tooltip="xNF Name" + class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <input type="text" class="text form-control" ng-model="ctrl.xnf_name" required> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">Test Type</label> + <i uib-tooltip="xNF Type" class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <select class="form-control" ng-model="ctrl.xnf_type" ng-init="ctrl.xnf_type='VNF'" required> + <option value="VNF">VNF</option> + </select> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">xNF Description</label> + <i uib-tooltip="xNF Description" + class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <input type="text" class="text form-control" ng-model="ctrl.xnf_description" required> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">xNFD Id</label> + <i uib-tooltip="xNFD Id" class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <input type="text" class="text form-control" ng-model="ctrl.xnfd_id" required> + </div> + </div> + <div class="field text col-md-4"> + <label class="left">Test Category</label> + <i uib-tooltip="Define test category<" class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <div class="middleColumn"> + <select class="form-control" ng-model="ctrl.xnf_test_period" ng-init="ctrl.xnf_test_period='Packaging Compliance Test'" required> + <option value="Packaging Compliance Test">Packaging Compliance Test</option> + <option value="Lifecycle">Lifecycle Test</option> + <option value="Functional">Functional Test</option> + <option value="Performance">Performance Test</option> + </select> + </div> + </div> + <div ng-if="ctrl.lab_location=='third'" class="field text"> + <div class="field text col-md-4"> + <label class="left">Lab Name</label> + <i uib-tooltip="Lab Name" class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <input type="text" class="text form-control" ng-model="ctrl.lab_name" required> + </div> + <div class="field text col-md-4"> + <label class="left">Lab Email</label> + <i uib-tooltip="Lab Email" class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <input type="email" class="text form-control" ng-model="ctrl.lab_email" required> + </div> + <div class="field text col-md-4"> + <label class="left">Lab Address</label> + <i uib-tooltip="Lab Address" class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <input type="text" class="text form-control" ng-model="ctrl.lab_address" required> + </div> + <div class="field text col-md-4"> + <label class="left">Lab Phone Number</label> + <i uib-tooltip="Lab Phone Number" class="glyphicon glyphicon-question-sign opnfv-blue"></i> + <input type="tel" class="text form-control" ng-model="ctrl.lab_phone" required> + </div> + </div> + </fieldset> + </div> + </div> + <button class="btn btn-default" + ng-click="ctrl.openConfirmModal(ctrl.tempResult)">Submit</button> +</div> + +<style type="text/css"> + .ngdialog.custom-background .ngdialog-content { + background: #ffffff; + } +</style> diff --git a/3rd_party/static/onap-ui/components/results/modal/applicationView.html b/3rd_party/static/onap-ui/components/results/modal/applicationView.html new file mode 100644 index 0000000..79341f8 --- /dev/null +++ b/3rd_party/static/onap-ui/components/results/modal/applicationView.html @@ -0,0 +1,103 @@ +<div class="container-fluid common-main-container"> + <div class="top-site-banner"> + <div class="container"> + <p class="p1">Application Details</p> + </div> + </div> + + <div class="row" style="margin-top: 20px;"> + <div class="col-lg-12 container"> + <p class="message" style="display: none;"></p> + <fieldset> + <div class="field text col-md-4"> + <label class="left">Company Name</label> + <div class="middleColumn">{{ ctrl.application.company_name }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">Company Logo</label> + <div class="middleColumn">{{ ctrl.application.company_logo }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">Company Website</label> + <div class="middleColumn">{{ ctrl.application.company_website }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">ONAP Version</label> + <div class="middleColumn">{{ ctrl.application.onap_version }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">Primary Contact Name</label> + <div class="middleColumn">{{ ctrl.application.primary_contact_name }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">Primary Contact Number</label> + <div class="middleColumn">{{ ctrl.application.primary_phone_number }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">Primary Contact Email</label> + <div class="middleColumn">{{ ctrl.application.primary_business_email }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">Test Agency</label> + <div class="middleColumn">{{ ctrl.application.lab_location }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">xNF Version</label> + <div class="middleColumn">{{ ctrl.application.xnf_version }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">xNF Name</label> + <div class="middleColumn">{{ ctrl.application.xnf_name }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">Test Type</label> + <div class="middleColumn">{{ ctrl.application.xnf_type }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">xNF Description</label> + <div class="middleColumn">{{ ctrl.application.xnf_description }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">xNFD Id</label> + <div class="middleColumn">{{ ctrl.application.xnfd_id }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">xNF Model Language</label> + <div class="middleColumn">{{ ctrl.application.xnfd_model_lang }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">Test Category</label> + <div class="middleColumn">{{ ctrl.application.xnf_test_period }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">xNF Checksum (SHA256)</label> + <div class="middleColumn">{{ ctrl.application.xnf_checksum }}</div> + </div> + <div ng-if="ctrl.lab_location=='third'" class="field text"> + <div class="field text col-md-4"> + <label class="left">Lab Name</label> + <div>{{ ctrl.application.lab_name }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">Lab Email</label> + <div>{{ ctrl.application.lab_email }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">Lab Address</label> + <div>{{ ctrl.application.lab_address }}</div> + </div> + <div class="field text col-md-4"> + <label class="left">Lab Phone Number</label> + <div>{{ ctrl.application.lab_phone }}</div> + </div> + </div> + </fieldset> + </div> + </div> +</div> + +<style type="text/css"> + .ngdialog.custom-background .ngdialog-content { + background: #ffffff; + } +</style> diff --git a/3rd_party/static/onap-ui/components/results/modal/reviewsModal.html b/3rd_party/static/onap-ui/components/results/modal/reviewsModal.html new file mode 100644 index 0000000..c93d1ef --- /dev/null +++ b/3rd_party/static/onap-ui/components/results/modal/reviewsModal.html @@ -0,0 +1,41 @@ +<div class="container-fluid common-main-container"> + <div class="top-site-banner"> + <div class="container"> + <p class="p1">Community Reviews</p> + </div> + </div> + + <div class="row" style="margin-top: 20px;"> + <div class="col-lg-12 container"> + <div cg-busy="{promise:ctrl.reviewsRequest,message:'Loading'}"></div> + <div ng-show="ctrl.reviews" class="results-table" style="width: 100%; overflow-x: scroll;"> + <table ng-data="ctrl.reviews" ng-show="ctrl.reviews" class="table table-striped table-hover"> + <thead> + <tr> + <th>Reviewer</th> + <th>Linux Foundation OpenId</th> + <th>Email</th> + <th>Review Date</th> + <th>Outcome</th> + </tr> + </thead> + <tbody style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"> + <tr ng-repeat="(index, review) in ctrl.reviews"> + <td>{{ review.reviewer_name }}</td> + <td>{{ review.reviewer_openid }}</td> + <td>{{ review.reviewer_email }}</td> + <td>{{ review.creation_date | limitTo:19}}</td> + <td>{{ review.outcome }}</td> + </tr> + </tbody> + </table> + </div> + </div> + </div> +</div> + +<style type="text/css"> + .ngdialog.custom-background .ngdialog-content { + background: #ffffff; + } +</style> diff --git a/3rd_party/static/onap-ui/components/results/modal/sharedModal.html b/3rd_party/static/onap-ui/components/results/modal/sharedModal.html new file mode 100644 index 0000000..021a355 --- /dev/null +++ b/3rd_party/static/onap-ui/components/results/modal/sharedModal.html @@ -0,0 +1,16 @@ +<div> + <h4>Enter user name or email</h4> + <input type="text" ng-model="ctrl.userName"> + <div style="text-align: center; margin-top: 20px;"> + <button class="btn btn-default" ng-disabled="ctrl.userName==null || ctrl.userName==''" + ng-click="ctrl.addSharedUser(ctrl.tempResult, ctrl.userName)">Commit</button> + </div> +</div> + +<style> + input { + border-radius: 10px; + border: 1px solid #eeeeee; + width: 100%; + } +</style> diff --git a/3rd_party/static/onap-ui/components/results/results.html b/3rd_party/static/onap-ui/components/results/results.html new file mode 100644 index 0000000..ce43036 --- /dev/null +++ b/3rd_party/static/onap-ui/components/results/results.html @@ -0,0 +1,168 @@ +<div class="container-fluid common-main-container"> + <h3>{{ctrl.pageHeader}}</h3> + <p>{{ctrl.pageParagraph}}</p> + <form class="form-inline" ng-show="ctrl.isUserResults"> + <h4>Upload Results + <i class="glyphicon glyphicon-question-sign opnfv-blue" + uib-tooltip="results file is logs.xxx.tar.gz under your dovetail installation path"></i> + </h4> + <div class="form-group col-m-3"> + <input class="form-contrl btn btn-success-cust cvp-btn medium accent-color regular-button" type="file" + file-model="resultFile"> + </div> + <div class="form-group col-m-3"> + <a class="btn btn-success-cust cvp-btn medium accent-color regular-button" ng-click="ctrl.uploadFile()"> + <span>upload result</span> + </a> + </div> + <div> + <label>{{ctrl.uploadState}}</label> + </div> + </form> + <div class="row" style="margin-bottom: 24px;"></div> + <div cg-busy="{promise:ctrl.authRequest,message:'Loading'}"></div> + <div cg-busy="{promise:ctrl.resultsRequest,message:'Loading'}"></div> + + <div ng-show="ctrl.data" class="results-table" style="width: 100%; overflow-x: scroll;"> + <table ng-data="ctrl.data.result" ng-show="ctrl.data" class="table table-striped table-hover"> + <thead> + <tr> + <th>Upload Date</th> + <th>Test ID</th> + <th>ONAP Version</th> + <th>Modeling Language</th> + <th>Owner</th> + <th>File Name</th> + <th>Label</th> + <th>Status</th> + <th>Log</th> + <th>Application</th> + <th>Review Status</th> + <th class="col-md-2">Operation</th> + <th class="col-md-2">Share List</th> + </tr> + </thead> + + <tbody style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"> + <tr ng-repeat="(index, result) in ctrl.data.tests"> + <td>{{ result.upload_date | limitTo:19}}</td> + <td> + <a uib-tooltip="{{ result.id }}" tooltip-placement="top" tooltip-append-to-body="true" + ng-click="ctrl.gotoResultDetail(result.id, result._id)">{{ result.id | limitTo:8 }}</a> + </td> + <td>{{ result.version || "2019.04" }}</td> + <td>{{ result.vnf_type.toUpperCase() }}</td> + <td>{{ result.owner }}</td> + <td>{{ result.filename || "None"}}</td> + <td> + <div class="popover-wrapper"> + <a editable-theme="bs3" onbeforesave="ctrl.changeLabel(result, 'label', $data)" + editable-text="result.label">{{ result.label || "None" }}</a> + </div> + </td> + <td>{{ result.status }}</td> + <td><a ng-click="ctrl.downloadLogs(result.id)">logs</a></td> + <td> + <a ng-if="result.status !='private'" ng-click="ctrl.openApplicationView(result)">View Application</a> + <div ng-if="result.status == 'private'">Not created</div> + </td> + <td><a ng-if="result.status !='private'" ng-click="ctrl.openReviewsModal(result.id)">View Reviews</a> + <div ng-if="result.status == 'private'"></div> + </td> + <td> + <div class="btn-group" uib-dropdown> + <a id="single-button" type="button" class="btn btn-success-cust cvp-btn medium accent-color regular-button" + uib-dropdown-toggle> + Operation<span class="caret"></span> + </a> + <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="single-button"> + <li role="menuitem" ng-if="auth.currentUser.openid == result.owner && ctrl.isUserResults" + class="menu-item menu-item-type-post_type menu-item-object-page"> + <a ng-class="{'hide': result.status != 'review'}" + ng-click="ctrl.deleteApplication(result)">withdraw submit</a> + </li> + <li role="menuitem" ng-if="auth.currentUser.openid == result.owner && ctrl.isUserResults" + class="menu-item menu-item-type-post_type menu-item-object-page"> + <a ng-class="{'hide': result.status != 'private'}" + ng-click="ctrl.openApplicationModal(result)">submit to review</a> + </li> + <li role="menuitem" + ng-if="auth.currentUser.role.indexOf('reviewer') != -1 && !ctrl.isUserResults" + class="menu-item menu-item-type-post_type menu-item-object-page"> + <a ng-class="{'hide': (result.voted == 'true') || (result.status != 'review')}" + ng-click="ctrl.toApprove(result)">approve</a> + </li> + <li role="menuitem" + ng-if="auth.currentUser.role.indexOf('reviewer') != -1 && !ctrl.isUserResults" + class="menu-item menu-item-type-post_type menu-item-object-page"> + <a ng-class="{'hide': (result.voted == 'true') || (result.status != 'review')}" + ng-click="ctrl.toDisapprove(result)">not approve</a> + </li> + <li role="menuitem" + ng-if="auth.currentUser.role.indexOf('reviewer') != -1 && !ctrl.isUserResults" + class="menu-item menu-item-type-post_type menu-item-object-page"> + <a ng-class="{'hide': (result.voted == 'false') || (result.status != 'review')}" + ng-click="ctrl.toUndo(result)">undo</a> + </li> + <li role="menuitem" ng-if="auth.currentUser.openid == result.owner && ctrl.isUserResults" + class="menu-item menu-item-type-post_type menu-item-object-page"> + <a ng-click="ctrl.openSharedModal(result)">share with</a> + </li> + <li role="menuitem" ng-if="auth.currentUser.openid == result.owner && ctrl.isUserResults" + class="menu-item menu-item-type-post_type menu-item-object-page"> + <a ng-click="ctrl.deleteTest(result._id)">delete</a> + </li> + </ul> + </div> + </td> + <td> + <div class="btn-group" uib-dropdown> + <a id="single-button-two" type="button" + class="btn btn-success-cust cvp-btn medium accent-color regular-button" style="width: 130px;" + uib-dropdown-toggle> + Share List<span class="caret"></span> + </a> + <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="single-button-two" + style="min-width: 200%;"> + <li class="menu-item menu-item-type-post_type menu-item-object-page" role="menuitem" + ng-repeat="share in result.shared track by $index"> + <span> + {{ share }} + <i ng-if="auth.currentUser.openid == result.owner" class="pull-right glyphicon glyphicon-remove" + ng-click="ctrl.removeSharedUser(result, share)"></i> + </span> + </li> + </ul> + </div> + </td> + </tr> + </tbody> + </table> + <div class="pages"> + <uib-pagination + total-items="ctrl.totalItems" + ng-model="ctrl.currentPage" + items-per-page="ctrl.itemsPerPage" + max-size="ctrl.maxSize" + class="pagination-sm" + boundary-links="true" + rotate="false" + num-pages="ctrl.numPages" + ng-change="ctrl.update()"> + </uib-pagination> + </div> + </div> +</div> + +<div ng-show="ctrl.showError" class="alert alert-danger" role="alert"> + <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> + <span class="sr-only">Error:</span> + {{ctrl.error}} +</div> + + +<style> + .button-disabled { + pointer-events: none; + } +</style> diff --git a/3rd_party/static/onap-ui/components/results/resultsController.js b/3rd_party/static/onap-ui/components/results/resultsController.js new file mode 100644 index 0000000..d459495 --- /dev/null +++ b/3rd_party/static/onap-ui/components/results/resultsController.js @@ -0,0 +1,614 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('ResultsController', ResultsController); + + angular + .module('testapiApp') + .directive('fileModel', ['$parse', function ($parse) { + return { + restrict: 'A', + link: function(scope, element, attrs) { + var model = $parse(attrs.fileModel); + var modelSetter = model.assign; + + element.bind('change', function(){ + scope.$apply(function(){ + modelSetter(scope, element[0].files[0]); + }); + }); + } + }; + }]); + + angular + .module('testapiApp') + .directive('modalFileModel', ['$parse', function ($parse) { + return { + restrict: 'A', + link: function(scope, element, attrs) { + var model = $parse(attrs.modalFileModel); + var modelSetter = model.assign; + + element.bind('change', function(){ + scope.$apply(function(){ + modelSetter(scope.$parent, element[0].files[0]); + }); + }); + } + }; + }]); + + ResultsController.$inject = [ + '$scope', '$http', '$filter', '$state', 'testapiApiUrl','raiseAlert', 'ngDialog', '$resource' + ]; + + /** + * TestAPI Results Controller + * This controller is for the '/results' page where a user can browse + * a listing of community uploaded results. + */ + function ResultsController($scope, $http, $filter, $state, testapiApiUrl, raiseAlert, ngDialog, $resource) { + var ctrl = this; + + ctrl.uploadFile=uploadFile; + ctrl.update = update; + ctrl.open = open; + ctrl.clearFilters = clearFilters; + ctrl.associateMeta = associateMeta; + ctrl.gotoResultDetail = gotoResultDetail; + ctrl.toggleCheck = toggleCheck; + ctrl.changeLabel = changeLabel; + ctrl.toApprove = toApprove; + ctrl.toDisapprove = toDisapprove; + ctrl.toUndo = toUndo; + ctrl.toReview = toReview; + ctrl.toPrivate = toPrivate; + ctrl.removeSharedUser = removeSharedUser; + ctrl.addSharedUser = addSharedUser; + ctrl.openSharedModal = openSharedModal; + ctrl.downloadLogs = downloadLogs; + ctrl.deleteApplication = deleteApplication; + ctrl.deleteTest = deleteTest; + ctrl.openApplicationModal = openApplicationModal; + ctrl.openApplicationView = openApplicationView; + ctrl.submitApplication = submitApplication; + ctrl.openConfirmModal = openConfirmModal; + ctrl.openReviewsModal = openReviewsModal; + + /** Mappings of Interop WG components to marketing program names. */ + ctrl.targetMappings = { + 'platform': 'Openstack Powered Platform', + 'compute': 'OpenStack Powered Compute', + 'object': 'OpenStack Powered Object Storage' + }; + + /** Initial page to be on. */ + ctrl.currentPage = 1; + + /** + * How many results should display on each page. Since pagination + * is server-side implemented, this value should match the + * 'results_per_page' configuration of the TestAPI server which + * defaults to 20. + */ + ctrl.itemsPerPage = 20; + + /** + * How many page buttons should be displayed at max before adding + * the '...' button. + */ + ctrl.maxSize = 5; + + /** The upload date lower limit to be used in filtering results. */ + ctrl.startDate = ''; + + /** The upload date upper limit to be used in filtering results. */ + ctrl.endDate = ''; + + /** The date format for the date picker. */ + ctrl.format = 'yyyy-MM-dd'; + + ctrl.userName = null; + + /** Check to see if this page should display user-specific results. */ + ctrl.isUserResults = $state.current.name === 'userResults'; + + /** Check to see if this page should display community results. */ + ctrl.isReviewer = $scope.auth.currentUser.role.indexOf('reviewer') != -1; + ctrl.isAdministrator = $scope.auth.currentUser.role.indexOf('administrator') != -1; + + ctrl.currentUser = $scope.auth.currentUser ? $scope.auth.currentUser.openid : null; + + // Should only be on user-results-page if authenticated. + if (!$scope.auth.isAuthenticated) { + $state.go('home'); + } + // Should only be on community-results if reviewer + if (!ctrl.isUserResults && !ctrl.isReviewer) { + $state.go('home'); + } + + ctrl.pageHeader = ctrl.isUserResults ? + 'Private test results' : 'Community test results'; + + ctrl.pageParagraph = ctrl.isUserResults ? + 'Your most recently uploaded test results are listed here.' : + 'The most recently uploaded community test results are listed ' + + 'here.'; + + ctrl.uploadState = ''; + + ctrl.authRequest = $scope.auth.doSignCheck().then(ctrl.update); + + function downloadLogs(id) { + // var logsUrl = testapiApiUrl + "/logs/log_" + id+".tar.gz"; + var logsUrl = "/logs/" + id + "/results/"; + window.location.href = logsUrl; + // $http.get(logsUrl); + } + + function deleteTest(inner_id) { + var resp = confirm('Are you sure to delete this test?'); + if (!resp) + return; + + var delUrl = testapiApiUrl + "/onap/tests/" + inner_id; + $http.get(delUrl) + .then( function(resp) { + var results = resp.data.results; + $http.delete(delUrl) + .then( function(ret) { + if(ret.data.code && ret.data.code != 0) { + alert(ret.data.msg); + return; + } + ctrl.update(); + angular.forEach(results, function(ele) { + delUrl = testapiApiUrl + "/results/" + ele; + $http.delete(delUrl); + }); + }); + }); + } + + function deleteApplication (result) { + var resp = confirm('Are you sure you want to delete this application?'); + if (!resp) + return; + + $http.get(testapiApiUrl + "/onap/cvp/applications?test_id=" + result.id).then(function(response) { + ctrl.application = response.data.applications[0]; + var app_id = ctrl.application._id; + var delUrl = testapiApiUrl + "/cvp/applications/" + app_id; + $http.delete(delUrl) + .then(function(ret) { + if (ret.data.code && ret.data.code != 0) { + alert(ret.data.msg); + return; + } + result['status'] = 'private'; + }); + + }, function(error) { + /* do nothing */ + }); + + } + + function submitApplication(result) { + var file = $scope.logoFile; + var logo_name = null; + if (typeof file !== 'undefined') { + + var fd = new FormData(); + fd.append('file', file); + fd.append('company_name', ctrl.company_name) + + $http.post(testapiApiUrl + "/cvp/applications/uploadlogo", fd, { + transformRequest: angular.identity, + headers: {'Content-Type': undefined} + }).then(function(resp) { + if (resp.data.code && resp.data.code != 0) { + alert(resp.data.msg); + return; + } else { + logo_name = resp.data.filename; + var data = { + "description": ctrl.description, + "onap_version": result.version, + "company_name": ctrl.company_name, + "company_logo": logo_name, + "company_website": ctrl.company_website, + "approve_date": "", + "approved": "false", + "test_id": result.id, + "lab_location": ctrl.lab_location, + "lab_email": ctrl.lab_email, + "lab_address": ctrl.lab_address, + "lab_phone": ctrl.lab_phone, + "xnf_version": ctrl.xnf_version, + "certification_type": ctrl.certification_type, + "xnf_name": ctrl.xnf_name, + "xnf_type": ctrl.xnf_type, + "xnf_description": ctrl.xnf_description, + "xnfd_id": ctrl.xnfd_id, + "xnfd_model_lang": result.vnf_type.toUpperCase(), + "xnf_checksum": result.vnf_checksum, + "xnf_test_period": ctrl.xnf_test_period, + "primary_contact_name": ctrl.primary_contact_name, + "primary_phone_number": ctrl.primary_phone_number, + "primary_business_email": ctrl.primary_business_email + }; + + $http.post(testapiApiUrl + "/onap/cvp/applications", data).then(function(resp) { + if (resp.data.code && resp.data.code != 0) { + alert(resp.data.msg); + return; + } + toggleCheck(result, 'status', 'review'); + }, function(error) { + /* do nothing */ + }); + } + }, function(error) { + /* do nothing */ + }); + logo_name = file.name; + } + ngDialog.close(); + } + + function openConfirmModal(result) { + var resp = confirm("Are you sure to submit?"); + if (resp) { + ctrl.submitApplication(result); + } + } + + function openApplicationModal(result) { + ctrl.tempResult = result; + ngDialog.open({ + preCloseCallback: function(value) { + }, + template: 'onap-ui/components/results/modal/applicationModal.html', + scope: $scope, + className: 'ngdialog-theme-default custom-background', + width: 950, + showClose: true, + closeByDocument: true + }); + } + + function openApplicationView(result) { + + $http.get(testapiApiUrl + "/onap/cvp/applications?test_id=" + result.id).then(function(response) { + ctrl.application = response.data.applications[0]; + }, function(error) { + /* do nothing */ + }); + + ctrl.tempResult = result; + ngDialog.open({ + preCloseCallback: function(value) { + }, + template: 'onap-ui/components/results/modal/applicationView.html', + scope: $scope, + className: 'ngdialog-theme-default custom-background', + width: 950, + showClose: true, + closeByDocument: true + }); + } + + function getReviews(test) { + var reviews_url = testapiApiUrl + '/onap/reviews?test_id=' + test; + ctrl.reviewsRequest = + $http.get(reviews_url).success(function (data) { + ctrl.reviews = data.reviews; + }).error(function (error) { + ctrl.reviews = null; + }); + } + + function openReviewsModal(test) { + getReviews(test); + ngDialog.open({ + preCloseCallback: function(value) { + }, + template: 'onap-ui/components/results/modal/reviewsModal.html', + scope: $scope, + className: 'ngdialog-theme-default custom-background', + width: 950, + showClose: true, + closeByDocument: true + }); + } + + function toggleCheck(result, item, newValue) { + var id = result._id; + var updateUrl = testapiApiUrl + "/onap/tests/"+ id; + + var data = {}; + data['item'] = item; + data[item] = newValue; + + $http.put(updateUrl, JSON.stringify(data), { + transformRequest: angular.identity, + headers: {'Content-Type': 'application/json'}}).then(function(ret) { + if(ret.data.code && ret.data.code != 0) { + alert(ret.data.msg); + } else { + result[item] = newValue; + } + }, function(error) { + alert('Error when update data'); + }); + } + + function changeLabel(result, key, data){ + if (result[key] !== data) { + toggleCheck(result, key, data); + } + } + + function doReview(test, outcome) { + var createUrl = testapiApiUrl + "/onap/reviews"; + var data = { + 'test_id': test.id, + 'outcome': outcome + }; + + $http.post(createUrl, JSON.stringify(data), { + transformRequest: angular.identity, + headers: {'Content-Type': 'application/json'}}).then(function(ret) { + if (ret.data.code && ret.data.code != 0) { + alert(ret.data.msg); + } else { + if (outcome === null) { + test.voted = 'false'; + } else { + test.voted = 'true'; + } + } + }, function(error) { + alert('Error when creating review'); + }); + } + + function toApprove(test) { + var resp = confirm('Once you approve a test result, your action will become visible. Do you want to proceed?'); + if (resp) { + doReview(test, 'positive'); + } + } + + function toDisapprove(test) { + var resp = confirm('Once you disapprove a test result, your action will become visible. Do you want to proceed?'); + if (resp) { + doReview(test, 'negative'); + } + } + + function toUndo(test) { + var resp = confirm('Once you undo your previous vote, your action will become visible. Do you want to proceed?'); + if (resp) { + doReview(test, null); + } + } + + function toReview(result, value){ + var resp = confirm('Once you submit a test result for review, it will become readable to all ONAPVP reviewers. Do you want to proceed?'); + if(resp){ + toggleCheck(result, 'status', value); + } + } + + function toPrivate(result, value){ + var resp = confirm('Do you want to proceed?'); + if(resp){ + toggleCheck(result, 'status', value); + } + } + + function openSharedModal(result){ + ctrl.tempResult = result; + ngDialog.open({ + preCloseCallback: function(value) { + }, + template: 'onap-ui/components/results/modal/sharedModal.html', + scope: $scope, + className: 'ngdialog-theme-default', + width: 950, + showClose: true, + closeByDocument: true + }); + } + + function addSharedUser(result, userId){ + var tempList = copy(result.shared); + tempList.push(userId); + toggleCheck(result, 'shared', tempList); + ngDialog.close(); + } + + function removeSharedUser(result, userId){ + var tempList = copy(result.shared); + var idx = tempList.indexOf(userId); + if(idx != -1){ + tempList.splice(idx, 1); + toggleCheck(result, 'shared', tempList); + } + } + + function copy(arrList){ + var tempList = []; + angular.forEach(arrList, function(ele){ + tempList.push(ele); + }); + return tempList; + } + + function uploadFileToUrl(file, uploadUrl){ + var fd = new FormData(); + fd.append('file', file); + + $http.post(uploadUrl, fd, { + transformRequest: angular.identity, + headers: {'Content-Type': undefined} + }).then(function(data){ + + if(data.data.code && data.data.code != 0){ + alert(data.data.msg); + return; + } + + ctrl.uploadState = ""; + data.data.filename = file.name; + var createTestUrl = testapiApiUrl + "/onap/tests" + + $http.post(createTestUrl, data.data).then(function(data){ + if (data.data.code && data.data.code != 0) { + alert(data.data.msg); + } else { + ctrl.update(); + } + }, function(error){ + }); + + }, function(error){ + ctrl.uploadState = "Upload failed. Error code is " + error.status; + }); + } + + function uploadFile(){ + var file = $scope.resultFile; + + var uploadUrl = testapiApiUrl + "/onap/results/upload"; + uploadFileToUrl(file, uploadUrl); + }; + + /** + * This will contact the TestAPI API to get a listing of test run + * results. + */ + function update() { + ctrl.showError = false; + // Construct the API URL based on user-specified filters. + var content_url = testapiApiUrl + '/onap/tests'; + var start = $filter('date')(ctrl.startDate, 'yyyy-MM-dd'); + var end = $filter('date')(ctrl.endDate, 'yyyy-MM-dd'); + + content_url += '?page=' + ctrl.currentPage; + content_url += '&per_page=' + ctrl.itemsPerPage; + if (start) { + content_url += '&from=' + start + ' 00:00:00'; + } + if (end) { + content_url += '&to=' + end + ' 23:59:59'; + } + if (ctrl.isUserResults) { + content_url += '&signed'; + } else { + content_url += '&status={"$ne":"private"}&review'; + } + + ctrl.resultsRequest = + $http.get(content_url).success(function (data) { + ctrl.data = data; + ctrl.totalItems = ctrl.data.pagination.total_pages * ctrl.itemsPerPage; + ctrl.currentPage = ctrl.data.pagination.current_page; + ctrl.numPages = ctrl.data.pagination.total_pages; + }).error(function (error) { + ctrl.data = null; + ctrl.totalItems = 0; + ctrl.showError = true; + ctrl.error = + 'Error retrieving results listing from server: ' + + angular.toJson(error); + }); + } + + /** + * This is called when the date filter calendar is opened. It + * does some event handling, and sets a scope variable so the UI + * knows which calendar was opened. + * @param {Object} $event - The Event object + * @param {String} openVar - Tells which calendar was opened + */ + function open($event, openVar) { + $event.preventDefault(); + $event.stopPropagation(); + ctrl[openVar] = true; + } + + /** + * This function will clear all filters and update the results + * listing. + */ + function clearFilters() { + ctrl.startDate = null; + ctrl.endDate = null; + ctrl.update(); + } + + /** + * This will send an API request in order to associate a metadata + * key-value pair with the given testId + * @param {Number} index - index of the test object in the results list + * @param {String} key - metadata key + * @param {String} value - metadata value + */ + function associateMeta(index, key, value) { + var testId = ctrl.data.results[index].id; + var metaUrl = [ + testapiApiUrl, '/results/', testId, '/meta/', key + ].join(''); + + var editFlag = key + 'Edit'; + if (value) { + ctrl.associateRequest = $http.post(metaUrl, value) + .success(function () { + ctrl.data.results[index][editFlag] = false; + }).error(function (error) { + raiseAlert('danger', error.title, error.detail); + }); + } + else { + ctrl.unassociateRequest = $http.delete(metaUrl) + .success(function () { + ctrl.data.results[index][editFlag] = false; + }).error(function (error) { + if (error.code == 404) { + // Key doesn't exist, so count it as a success, + // and don't raise an alert. + ctrl.data.results[index][editFlag] = false; + } + else { + raiseAlert('danger', error.title, error.detail); + } + }); + } + } + + function gotoResultDetail(testId, innerID) { + $state.go('resultsDetail', {'testID': testId, 'innerID': innerID}); + } + } +})(); diff --git a/3rd_party/static/onap-ui/config.json b/3rd_party/static/onap-ui/config.json new file mode 100644 index 0000000..ab9c32d --- /dev/null +++ b/3rd_party/static/onap-ui/config.json @@ -0,0 +1 @@ +{"testapiApiUrl": "/api/v1"} diff --git a/3rd_party/static/onap-ui/favicon.ico b/3rd_party/static/onap-ui/favicon.ico Binary files differnew file mode 100644 index 0000000..bbcf83c --- /dev/null +++ b/3rd_party/static/onap-ui/favicon.ico diff --git a/3rd_party/static/onap-ui/index.html b/3rd_party/static/onap-ui/index.html new file mode 100644 index 0000000..55ba933 --- /dev/null +++ b/3rd_party/static/onap-ui/index.html @@ -0,0 +1,79 @@ +<!DOCTYPE html> +<!-- + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. +--> +<html id="ng-app"> + <head> + <meta charset="utf-8"> + <meta name="description" content="TestAPI"> + <meta name="viewport" content="width=device-width"> + <title>ONAP</title> + + <link rel="icon" type="image/png" href="onap-ui/favicon.ico" sizes="16x16"> + <link rel="icon" type="image/png" href="onap-ui/favicon.ico" sizes="32x32"> + + <!-- CSS Libraries --> + <link rel="stylesheet" href="onap-ui/node_modules/bootstrap/dist/css/bootstrap.min.css"> + <link rel="stylesheet" href="onap-ui/node_modules/angular-busy/dist/angular-busy.min.css"> + <link rel="stylesheet" href="onap-ui/node_modules/ng-dialog/css/ngDialog.min.css"> + <link rel="stylesheet" href="onap-ui/node_modules/ng-dialog/css/ngDialog-theme-default.min.css"> + <link rel="stylesheet" href="onap-ui/node_modules/angular-xeditable/dist/css/xeditable.min.css"> + + <!-- CSS Internal Code --> + <link rel="stylesheet" href="onap-ui/assets/css/cvp-style.css"> + <link rel="stylesheet" href="onap-ui/assets/css/ascend.css"> + <link rel="stylesheet" href="onap-ui/assets/css/index.css"> + <link rel="stylesheet" href="onap-ui/assets/css/header.css"> + <link rel="stylesheet" href="onap-ui/assets/css/home/home.css"> + <link rel="stylesheet" href="onap-ui/assets/css/combine.css"> + + <!-- JS Libraries --> + <script src="onap-ui/node_modules/jquery/dist/jquery.min.js"></script> + <script src="onap-ui/node_modules/bootstrap/dist/js/bootstrap.min.js"></script> + <script src="onap-ui/node_modules/angular/angular.min.js"></script> + <script src="onap-ui/node_modules/angular-ui-router/release/angular-ui-router.min.js"></script> + <script src="onap-ui/node_modules/angular-resource/angular-resource.min.js"></script> + <script src="onap-ui/node_modules/angular-ui-bootstrap/ui-bootstrap-tpls.min.js"></script> + <script src="onap-ui/node_modules/angular-busy/dist/angular-busy.min.js"></script> + <script src="onap-ui/node_modules/angular-confirm/angular-confirm.min.js"></script> + <script src="onap-ui/node_modules/ng-dialog/js/ngDialog.min.js"></script> + <script src="onap-ui/node_modules/angular-xeditable/dist/js/xeditable.min.js"></script> + + <!-- Application Root --> + <script src="onap-ui/app.js"></script> + + <!-- Controllers --> + <script src="onap-ui/shared/header/headerController.js"></script> + <script src="onap-ui/shared/alerts/alertModalFactory.js"></script> + <script src="onap-ui/components/home/homeController.js"></script> + <script src="onap-ui/components/directory/directoryController.js"></script> + <script src="onap-ui/components/results/resultsController.js"></script> + <script src="onap-ui/components/results-report/resultsReportController.js"></script> + <script src="onap-ui/components/application/applicationController.js"></script> + <script src="onap-ui/components/profile/profileController.js"></script> + <script src="onap-ui/components/auth-failure/authFailureController.js"></script> + <script src="onap-ui/components/logout/logoutController.js"></script> + + <!-- Filters --> + <script src="onap-ui/shared/filters.js"></script> + + </head> + + <body class="container-fluid home page-template-default page page-id-6 do-etfw tribe-no-js ascend wpb-js-composer + js-comp-ver-5.2.1 vc_responsive index-header" + ng-controller="HeaderController as header"> + <header ng-include src="'onap-ui/shared/header/header.html'"></header> + <div ui-view></div> + <footer ng-include src="'onap-ui/shared/footer/footer.html'"></footer> + </body> +</html> diff --git a/3rd_party/static/onap-ui/package.json b/3rd_party/static/onap-ui/package.json new file mode 100644 index 0000000..128d354 --- /dev/null +++ b/3rd_party/static/onap-ui/package.json @@ -0,0 +1,36 @@ +{ + "name": "onap-ui", + "version": "2.0.0", + "description": "Frontend Angular Application for ONAP Verified Program", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://git.opnfv.org/dovetail-webportal.git" + }, + "keywords": [ + "ONAP" + ], + "author": "Stamatis Katsaounis", + "license": "Apache-2.0", + "bugs": { + "url": "https://jira.opnfv.org/projects/DOVETAIL/issues/" + }, + "homepage": "https://wiki.opnfv.org/display/dovetail/Dovetail+Home", + "dependencies": { + "angular": "1.3.15", + "angular-animate": "1.3.20", + "angular-busy": "4.1.3", + "angular-confirm": "1.2.3", + "angular-mocks": "1.3.15", + "angular-resource": "1.3.15", + "angular-ui-bootstrap": "0.14.3", + "angular-ui-router": "0.2.13", + "angular-xeditable": "0.8.0", + "bootstrap": "3.3.2", + "jquery": "3.2.1", + "ng-dialog": "1.4.0" + } +} diff --git a/3rd_party/static/onap-ui/robots.txt b/3rd_party/static/onap-ui/robots.txt new file mode 100644 index 0000000..93c4420 --- /dev/null +++ b/3rd_party/static/onap-ui/robots.txt @@ -0,0 +1,4 @@ +# robotstxt.org + +User-agent: * + diff --git a/3rd_party/static/onap-ui/shared/alerts/alertModal.html b/3rd_party/static/onap-ui/shared/alerts/alertModal.html new file mode 100644 index 0000000..5d1a097 --- /dev/null +++ b/3rd_party/static/onap-ui/shared/alerts/alertModal.html @@ -0,0 +1,8 @@ +<div class="modal-body" style="padding:0px"> + <div class="alert alert-{{alert.data.mode}}" style="margin-bottom:0px"> + <button type="button" class="close" data-ng-click="alert.close()" > + <span class="glyphicon glyphicon-remove-circle"></span> + </button> + <strong>{{alert.data.title}}</strong> {{alert.data.text}} + </div> +</div> diff --git a/3rd_party/static/onap-ui/shared/alerts/alertModalFactory.js b/3rd_party/static/onap-ui/shared/alerts/alertModalFactory.js new file mode 100644 index 0000000..49dd5fa --- /dev/null +++ b/3rd_party/static/onap-ui/shared/alerts/alertModalFactory.js @@ -0,0 +1,74 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .factory('raiseAlert', raiseAlert); + + raiseAlert.$inject = ['$uibModal']; + + /** + * This allows alert pop-ups to be raised. Just inject it as a dependency + * in the calling controller. + */ + function raiseAlert($uibModal) { + return function(mode, title, text) { + $uibModal.open({ + templateUrl: 'onap-ui/shared/alerts/alertModal.html', + controller: 'RaiseAlertModalController as alert', + backdrop: true, + keyboard: true, + backdropClick: true, + size: 'md', + resolve: { + data: function () { + return { + mode: mode, + title: title, + text: text + }; + } + } + }); + }; + } + + angular + .module('testapiApp') + .controller('RaiseAlertModalController', RaiseAlertModalController); + + RaiseAlertModalController.$inject = ['$uibModalInstance', 'data']; + + /** + * This is the controller for the alert pop-up. + */ + function RaiseAlertModalController($uibModalInstance, data) { + var ctrl = this; + + ctrl.close = close; + ctrl.data = data; + + /** + * This method will close the alert modal. The modal will close + * when the user clicks the close button or clicks outside of the + * modal. + */ + function close() { + $uibModalInstance.close(); + } + } +})(); diff --git a/3rd_party/static/onap-ui/shared/filters.js b/3rd_party/static/onap-ui/shared/filters.js new file mode 100644 index 0000000..538c02e --- /dev/null +++ b/3rd_party/static/onap-ui/shared/filters.js @@ -0,0 +1,100 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + /** + * Convert an object of objects to an array of objects to use with ng-repeat + * filters. + */ + angular + .module('testapiApp') + .filter('arrayConverter', arrayConverter); + + /** + * Convert an object of objects to an array of objects to use with ng-repeat + * filters. + */ + function arrayConverter() { + return function (objects) { + var array = []; + angular.forEach(objects, function (object, key) { + if (!('id' in object)) { + object.id = key; + } + array.push(object); + }); + return array; + }; + } + + angular + .module('testapiApp') + .filter('capitalize', capitalize); + + /** + * Angular filter that will capitalize the first letter of a string. + */ + function capitalize() { + return function (string) { + return string.substring(0, 1).toUpperCase() + string.substring(1); + }; + } + + angular + .module('testapiApp') + .filter('tagExtractor', tagExtractor); + + function tagExtractor() { + return function (string) { + return string.substring(13, string.indexOf('dovetail')-1); + }; + } + + angular + .module('testapiApp') + .filter('checkFlag', checkFlag); + + function checkFlag() { + return function (string) { + return string == undefined || string == "true"; + }; + } + + angular + .module('testapiApp') + .filter('category', category); + + function category() { + return function (string) { + if (string == "soft&hard") + return "software and hardware"; + return "software and third party hardware"; + }; + } + + angular + .module('testapiApp') + .filter('labLocation', labLocation); + + function labLocation() { + return function (string) { + if (string == "internal") + return "internal vendor lab"; + return "third-party lab"; + }; + } + +})(); diff --git a/3rd_party/static/onap-ui/shared/footer/footer.html b/3rd_party/static/onap-ui/shared/footer/footer.html new file mode 100644 index 0000000..c0f1b70 --- /dev/null +++ b/3rd_party/static/onap-ui/shared/footer/footer.html @@ -0,0 +1,3 @@ +<div> + <span class="hide">version: web.cvp.0.7.0</span> +</div> diff --git a/3rd_party/static/onap-ui/shared/header/header.html b/3rd_party/static/onap-ui/shared/header/header.html new file mode 100644 index 0000000..3a87a73 --- /dev/null +++ b/3rd_party/static/onap-ui/shared/header/header.html @@ -0,0 +1,48 @@ +<div class="header-container-1"> + <div class="row header-container-row"> + <div class="col-md-3"> + <a href="/#/"> + <img class="stnd dark-version header-logo" alt="ONAP" src="onap-ui/assets/img/logo.png"> + </a> + </div> + <div class="col-md-offset-1 header-title"> + <span class="header-title">ONAP Verified Program</span> + </div> + </div> +</div> + +<div class="header-container-2"> + <div class="row header-container-row"> + <div> + <ul class="nav navbar-nav navbar-right header-login"> + <li ng-class="{ active: header.isActive('/application')}" + ng-if="auth.isAuthenticated && auth.currentUser.role.indexOf('administrator') != -1"> + <a ui-sref="application">Applications</a> + </li> + <li ng-class="{ active: header.isActive('/community_results')}" + ng-if="auth.isAuthenticated && auth.canReview(auth.currentUser)"> + <a ui-sref="communityResults">Incoming Reviews</a> + </li> + <li ng-class="{ active: header.isActive('/user_results')}" + ng-if="auth.isAuthenticated && auth.currentUser.role.indexOf('user') != -1"> + <a ui-sref="userResults">My Results</a> + </li> + <li ng-class="{ active: header.isActive('/profile')}" ng-if="auth.isAuthenticated"> + <a ui-sref="profile">Profile</a> + </li> + <li ng-if="auth.isAuthenticated" style="margin-right:10px"> + <a href="" ng-click="auth.doSignOut()">Sign Out</a> + </li> + <li ng-if="!auth.isAuthenticated" style="margin-right:10px;"> + <a href="" ng-click="auth.doSignIn('cas')"> + <span class="glyphicon glyphicon-user" aria-hidden="true"> </span>Sign In / Sign Up + </a> + </li> + </ul> + </div> + </div> +</div> + +<div class="container-fluid header-splitline"> + <div class="row header-container-row"></div> +</div> diff --git a/3rd_party/static/onap-ui/shared/header/headerController.js b/3rd_party/static/onap-ui/shared/header/headerController.js new file mode 100644 index 0000000..0a14a41 --- /dev/null +++ b/3rd_party/static/onap-ui/shared/header/headerController.js @@ -0,0 +1,63 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('HeaderController', HeaderController); + + HeaderController.$inject = ['$location']; + + /** + * TestAPI Header Controller + * This controller is for the header template which contains the site + * navigation. + */ + function HeaderController($location) { + var ctrl = this; + + ctrl.isActive = isActive; + ctrl.isCatalogActive = isCatalogActive; + + /** Whether the Navbar is collapsed for small displays. */ + ctrl.navbarCollapsed = true; + + /** + * This determines whether a button should be in the active state based + * on the URL. + */ + function isActive(viewLocation) { + var path = $location.path().substr(0, viewLocation.length); + if (path === viewLocation) { + // Make sure "/" only matches when viewLocation is "/". + if (!($location.path().substr(0).length > 1 && + viewLocation.length === 1 )) { + return true; + } + } + return false; + } + + /** This determines the active state for the catalog dropdown. Type + * parameter should be passed in to specify if the catalog is the + * public or user one. + */ + function isCatalogActive(type) { + return ctrl.isActive('/' + type + '_vendors') + || ctrl.isActive('/' + type + '_products'); + } + } +})(); diff --git a/docker/Dockerfile.api b/docker/Dockerfile.api index b414dd3..016fa33 100644 --- a/docker/Dockerfile.api +++ b/docker/Dockerfile.api @@ -44,6 +44,7 @@ RUN apt-get update && apt-get install -y \ libssl-dev \ libxml2-dev \ libxslt1-dev \ + libjpeg-dev \ --no-install-recommends \ && rm -rf /var/lib/apt/lists/* diff --git a/etc/config.ini b/etc/config.ini index 13e6de5..0bcae0c 100644 --- a/etc/config.ini +++ b/etc/config.ini @@ -92,4 +92,4 @@ OAUTH_CALLBACK_URL = http://localhost:9999/api/v1/auth/signin_return_jira [lfid] url = https://identity.linuxfoundation.org/cas/ -return_url = http://localhost:9999/api/v1/auth/signin_return_cas +return_url = api/v1/auth/signin_return_cas diff --git a/opnfv_testapi/cmd/server.py b/opnfv_testapi/cmd/server.py index fee5877..5dcc60a 100644 --- a/opnfv_testapi/cmd/server.py +++ b/opnfv_testapi/cmd/server.py @@ -31,6 +31,7 @@ TODOs : import tornado.ioloop import logging +import sys from opnfv_testapi.common.config import CONF from opnfv_testapi.router import url_mappings @@ -42,12 +43,19 @@ handler = logging.handlers.RotatingFileHandler( my_logger.setLevel(logging.DEBUG) my_logger.addHandler(handler) +ch = logging.StreamHandler(sys.stdout) +ch.setLevel(logging.DEBUG) +formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +my_logger.addHandler(ch) + def make_app(): swagger.docs(base_url=CONF.swagger_base_url, static_path=CONF.ui_static_path) return swagger.Application( - url_mappings.mappings, + url_mappings.mappings + url_mappings.onap_mappings, debug=CONF.api_debug, auth=CONF.api_authenticate, cookie_secret='opnfv-testapi', diff --git a/opnfv_testapi/resources/application_handlers.py b/opnfv_testapi/resources/application_handlers.py index 7cecd3e..7d823b8 100644 --- a/opnfv_testapi/resources/application_handlers.py +++ b/opnfv_testapi/resources/application_handlers.py @@ -6,15 +6,20 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## +from datetime import datetime import logging import json +import os from tornado import web from tornado import gen from bson import objectid +from slugify import slugify +from PIL import Image from opnfv_testapi.common.config import CONF from opnfv_testapi.common import utils +from opnfv_testapi.db import api as dbapi from opnfv_testapi.resources import handlers from opnfv_testapi.resources import application_models from opnfv_testapi.tornado_swagger import swagger @@ -34,19 +39,28 @@ class ApplicationsLogoHandler(GenericApplicationHandler): @web.asynchronous @gen.coroutine def post(self): - role = self.get_secure_cookie(auth_const.ROLE) - if role.find('administrator') == -1: - msg = 'Only administrator is allowed to upload logos' - self.finish_request({'code': '-1', 'msg': msg}) - return - fileinfo = self.request.files['file'][0] - fname = fileinfo['filename'] + company_logo_name = self.request.arguments['company_name'][0] + extension_name = fileinfo['filename'].split('.')[-1] + company_logo_name = slugify(company_logo_name) + fileinfo['filename'] = company_logo_name location = 'media/companies/' - fh = open(location + fname, 'w') + full_name_path = location + company_logo_name + '.' + extension_name + fh = open(full_name_path, 'w') fh.write(fileinfo['body']) - msg = 'Successfully uploaded logo: ' + fname - resp = {'code': '1', 'msg': msg} + fh.close() + img = Image.open(full_name_path) + if (img.size[0] > 165) or (img.size[1] > 40): + os.remove(full_name_path) + msg = 'The size of the image is not according to the compliance' \ + ' program. Please try again, loading an image with proper' \ + ' dimensions (Max Values: 165px width and 40px height).' + self.finish_request({'code': 403, 'msg': msg}) + return + + msg = 'Successfully uploaded logo: ' + company_logo_name + resp = {'code': 0, 'msg': msg, + 'filename': company_logo_name + '.' + extension_name} self.finish_request(resp) @@ -130,6 +144,8 @@ class ApplicationsCLHandler(GenericApplicationHandler): openid = self.get_secure_cookie(auth_const.OPENID) if openid: self.json_args['owner'] = openid + if self.is_onap: + self.json_args['is_onap'] = 'true' self._post() @@ -138,22 +154,21 @@ class ApplicationsCLHandler(GenericApplicationHandler): miss_fields = [] carriers = [] - role = self.get_secure_cookie(auth_const.ROLE) - if role.find('administrator') == -1: - self.finish_request({'code': '403', 'msg': 'Only administrator \ - is allowed to submit application.'}) - return - - query = {"openid": self.json_args['user_id']} - table = "users" - ret, msg = yield self._check_if_exists(table=table, query=query) + query = {'openid': self.json_args['owner']} + ret, msg = yield self._check_if_exists(table='users', query=query) logging.debug('ret:%s', ret) if not ret: - self.finish_request({'code': '403', 'msg': msg}) + self.finish_request({'code': 403, 'msg': msg}) + return + query = {'test_id': self.json_args['test_id']} + ret, _ = yield self._check_if_exists(table=self.table, query=query) + if ret: + msg = 'An application for these test results already exists' + self.finish_request({'code': 403, 'msg': msg}) return self._create(miss_fields=miss_fields, carriers=carriers) - self._send_email() + # self._send_email() def _send_email(self): @@ -173,7 +188,6 @@ This is a new application: Primary Email: {}, Primary Address: {}, Primary Phone: {}, - User ID Type: {}, User ID: {} Best Regards, @@ -188,19 +202,26 @@ CVP Team data.prim_email, data.prim_address, data.prim_phone, - data.id_type, - data.user_id) + data.owner) utils.send_email(subject, content) class ApplicationsGURHandler(GenericApplicationHandler): @swagger.operation(nickname="deleteAppById") + @gen.coroutine def delete(self, id): query = {'_id': objectid.ObjectId(id)} + application = yield dbapi.db_find_one(self.table, query) + test_id = application['test_id'] + t_query = {'id': test_id} + yield dbapi.db_delete('reviews', {'test_id': test_id}) + yield dbapi.db_update('tests', t_query, + {'$set': {'status': 'private'}}) self._delete(query=query) @swagger.operation(nickname="updateApplicationById") + @web.asynchronous def put(self, application_id): """ @description: update a single application by id @@ -222,12 +243,23 @@ class ApplicationsGURHandler(GenericApplicationHandler): logging.error('except:%s', e) return - @web.asynchronous @gen.coroutine def update(self, application_id, item, value): self.json_args = {} self.json_args[item] = value - query = {'_id': application_id, 'owner': + query = {'_id': objectid.ObjectId(application_id), 'owner': self.get_secure_cookie(auth_const.OPENID)} db_keys = ['_id', 'owner'] + if item == 'approved': + if value == 'true': + status = 'verified' + self.json_args['approve_date'] = str(datetime.now()) + else: + status = 'review' + self.json_args['approve_date'] = '' + application = yield dbapi.db_find_one(self.table, query) + test_id = application['test_id'] + t_query = {'id': test_id} + yield dbapi.db_update('tests', t_query, + {'$set': {'status': status}}) self._update(query=query, db_keys=db_keys) diff --git a/opnfv_testapi/resources/handlers.py b/opnfv_testapi/resources/handlers.py index e8c81f3..559e689 100644 --- a/opnfv_testapi/resources/handlers.py +++ b/opnfv_testapi/resources/handlers.py @@ -41,6 +41,7 @@ DEFAULT_REPRESENTATION = "application/json" class GenericApiHandler(web.RequestHandler): def __init__(self, application, request, **kwargs): + self.is_onap = False super(GenericApiHandler, self).__init__(application, request, **kwargs) self.json_args = None self.table = None @@ -52,6 +53,9 @@ class GenericApiHandler(web.RequestHandler): self.db_scenarios = 'scenarios' self.auth = self.settings["auth"] + def initialize(self, is_onap=False): + self.is_onap = is_onap + def get_int(self, key, value): try: value = int(value) @@ -97,6 +101,11 @@ class GenericApiHandler(web.RequestHandler): if role.find("reviewer") != -1: query['$or'].append({"status": {"$ne": "private"}}) + elif k == 'status': + if v.startswith('{'): + query[k] = json.loads(v) + else: + query[k] = v elif k not in ['last', 'page', 'descend', 'per_page']: query[k] = v if date_range: @@ -107,6 +116,8 @@ class GenericApiHandler(web.RequestHandler): if 'start_date' in query and '$lt' not in query['start_date']: query['start_date'].update({'$lt': str(datetime.now())}) + query['is_onap'] = 'true' if self.is_onap else None + logging.debug("query:%s", query) raise gen.Return((query)) @@ -184,7 +195,7 @@ class GenericApiHandler(web.RequestHandler): if query and table: data = yield dbapi.db_find_one(table, query) if data: - raise gen.Return((True, 'Data alreay exists. %s' % (query))) + raise gen.Return((True, 'Data already exists. %s' % (query))) raise gen.Return((False, 'Data does not exist. %s' % (query))) # @web.asynchronous @@ -214,7 +225,7 @@ class GenericApiHandler(web.RequestHandler): if res_op is None: res = {self.table: data} else: - res = res_op(data, *args) + res = yield res_op(data, *args) if page > 0: res.update({ 'pagination': { diff --git a/opnfv_testapi/resources/result_handlers.py b/opnfv_testapi/resources/result_handlers.py index 38109ad..9501bfd 100644 --- a/opnfv_testapi/resources/result_handlers.py +++ b/opnfv_testapi/resources/result_handlers.py @@ -20,6 +20,7 @@ from bson import objectid from opnfv_testapi.common.config import CONF from opnfv_testapi.common import message from opnfv_testapi.common import raises +from opnfv_testapi.db import api as dbapi from opnfv_testapi.resources import handlers from opnfv_testapi.resources import result_models from opnfv_testapi.tornado_swagger import swagger @@ -41,6 +42,7 @@ class GenericResultHandler(handlers.GenericApiHandler): raises.BadRequest(message.must_int(key)) return value + @gen.coroutine def set_query(self): query = dict() date_range = dict() @@ -82,11 +84,15 @@ class GenericResultHandler(handlers.GenericApiHandler): if 'start_date' in query and '$lt' not in query['start_date']: query['start_date'].update({'$lt': str(datetime.now())}) - return query + query['is_onap'] = 'true' if self.is_onap else None + + raise gen.Return((query)) class ResultsCLHandler(GenericResultHandler): @swagger.operation(nickname="queryTestResults") + @web.asynchronous + @gen.coroutine def get(self): """ @description: Retrieve result(s) for a test project @@ -195,7 +201,8 @@ class ResultsCLHandler(GenericResultHandler): 'per_page': CONF.api_results_per_page } - self._list(query=self.set_query(), **limitations) + query = yield self.set_query() + yield self._list(query=query, **limitations) @swagger.operation(nickname="createTestResult") def post(self): @@ -267,10 +274,19 @@ class ResultsUploadHandler(ResultsCLHandler): results = results.split('\n') result_ids = [] version = '' + vnf_type = None + vnf_checksum = None for result in results: if result == '': continue self.json_args = json.loads(result).copy() + openid = self.get_secure_cookie(auth_const.OPENID) + if openid: + self.json_args['owner'] = openid + if self.is_onap: + self.json_args['is_onap'] = 'true' + vnf_type = self.json_args['vnf_type'] + vnf_checksum = self.json_args['vnf_checksum'] # the result files used in the first release of OVP did not # specify an OVP version if (self.json_args['version'] == 'master' @@ -288,14 +304,30 @@ class ResultsUploadHandler(ResultsCLHandler): with open(log_filename, "wb") as tar_out: tar_out.write(fileinfo['body']) resp = {'id': test_id, 'results': result_ids, 'version': version} + if vnf_type: + resp['vnf_type'] = vnf_type + resp['vnf_checksum'] = vnf_checksum self.finish_request(resp) class ResultsGURHandler(GenericResultHandler): @swagger.operation(nickname='DeleteTestResultById') + @gen.coroutine def delete(self, result_id): - query = {'_id': objectid.ObjectId(result_id)} - self._delete(query=query) + curr_user = self.get_secure_cookie(auth_const.OPENID) + curr_user_role = self.get_secure_cookie(auth_const.ROLE) + if curr_user is not None: + query = {'_id': objectid.ObjectId(result_id)} + test_data = yield dbapi.db_find_one(self.table, query) + if not test_data: + raises.NotFound(message.not_found(self.table, query)) + if curr_user == test_data['owner'] or \ + curr_user_role.find('administrator') != -1: + self._delete(query=query) + else: + raises.Forbidden(message.no_auth()) + else: + raises.Unauthorized(message.no_auth()) @swagger.operation(nickname='getTestResultById') def get(self, result_id): diff --git a/opnfv_testapi/resources/review_handlers.py b/opnfv_testapi/resources/review_handlers.py new file mode 100644 index 0000000..9731e0f --- /dev/null +++ b/opnfv_testapi/resources/review_handlers.py @@ -0,0 +1,119 @@ +############################################################################## +# Copyright (c) 2019 Intracom Telecom +# mokats@intracom-telecom.com +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +from datetime import datetime +import logging + +from tornado import web, gen + +from opnfv_testapi.common.config import CONF +from opnfv_testapi.common import message, raises +from opnfv_testapi.db import api as dbapi +from opnfv_testapi.resources import handlers, review_models +from opnfv_testapi.tornado_swagger import swagger +from opnfv_testapi.ui.auth import constants as auth_const + + +class GenericReviewHandler(handlers.GenericApiHandler): + def __init__(self, review, request, **kwargs): + super(GenericReviewHandler, self).__init__(review, request, **kwargs) + self.table = 'reviews' + self.table_cls = review_models.Review + + +class ReviewsCLHandler(GenericReviewHandler): + @swagger.operation(nickname="queryReviews") + @web.asynchronous + @gen.coroutine + def get(self): + def descend_limit(): + descend = self.get_query_argument('descend', 'true') + return -1 if descend.lower() == 'true' else 1 + + def last_limit(): + return self.get_int('last', self.get_query_argument('last', 0)) + + def page_limit(): + return self.get_int('page', self.get_query_argument('page', 0)) + + limitations = { + 'sort': {'_id': descend_limit()}, + 'last': last_limit(), + 'page': page_limit(), + 'per_page': CONF.api_results_per_page + } + + query = yield self.set_query() + yield self._list(query=query, **limitations) + logging.debug('list end') + + @swagger.operation(nickname="createReview") + @web.asynchronous + def post(self): + openid = self.get_secure_cookie(auth_const.OPENID) + if openid: + self.json_args['reviewer_openid'] = openid + + if self.json_args['outcome'] is None: + self._del() + else: + self._post() + + @gen.coroutine + def _post(self): + query = {'openid': self.json_args['reviewer_openid']} + user = yield dbapi.db_find_one('users', query) + if not user: + raises.Forbidden(message.unauthorized()) + role = self.get_secure_cookie(auth_const.ROLE) + if 'reviewer' not in role.split(','): + raises.Unauthorized(message.no_auth()) + test = yield dbapi.db_find_one( + 'tests', {'id': self.json_args['test_id']}) + if test['owner'] == self.json_args['reviewer_openid']: + self.finish_request({'code': 403, + 'msg': 'No permision to review own results'}) + return + query = { + 'reviewer_openid': self.json_args['reviewer_openid'], + 'test_id': self.json_args['test_id'] + } + review = yield dbapi.db_find_one(self.table, query) + if review: + if review['outcome'] != self.json_args['outcome']: + yield dbapi.db_update(self.table, query, + {'$set': { + 'outcome': self.json_args['outcome'], + 'creation_date': datetime.now()}}) + self.finish_request() + else: + self.json_args['reviewer_name'] = user['fullname'] + self.json_args['reviewer_email'] = user['email'] + self._create(miss_fields=[], carriers=[]) + + @gen.coroutine + def _del(self): + query = {'openid': self.json_args['reviewer_openid']} + user = yield dbapi.db_find_one('users', query) + if not user: + raises.Forbidden(message.unauthorized()) + role = self.get_secure_cookie(auth_const.ROLE) + if 'reviewer' not in role.split(','): + raises.Unauthorized(message.no_auth()) + test = yield dbapi.db_find_one( + 'tests', {'id': self.json_args['test_id']}) + if test['owner'] == self.json_args['reviewer_openid']: + self.finish_request({'code': 403, + 'msg': 'No permision to review own results'}) + return + query = { + 'reviewer_openid': self.json_args['reviewer_openid'], + 'test_id': self.json_args['test_id'] + } + yield dbapi.db_delete(self.table, query) + self.finish_request() diff --git a/opnfv_testapi/resources/review_models.py b/opnfv_testapi/resources/review_models.py new file mode 100644 index 0000000..2aaa62c --- /dev/null +++ b/opnfv_testapi/resources/review_models.py @@ -0,0 +1,39 @@ +############################################################################## +# Copyright (c) 2019 +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +from opnfv_testapi.resources import models +from opnfv_testapi.tornado_swagger import swagger + +from datetime import datetime + + +@swagger.model() +class Review(models.ModelBase): + def __init__(self, _id=None, test_id=None, reviewer_openid=None, + reviewer_email=None, reviewer_name=None, creation_date=None, + outcome=None): + self._id = _id + self.test_id = test_id + self.reviewer_openid = reviewer_openid + self.reviewer_email = reviewer_email + self.reviewer_name = reviewer_name + self.creation_date = datetime.now() + self.outcome = outcome + + +@swagger.model() +class Reviews(models.ModelBase): + """ + @property reviews: + @ptype tests: C{list} of L{Review} + """ + def __init__(self): + self.reviews = list() + + @staticmethod + def attr_parser(): + return {'reviews': Review} diff --git a/opnfv_testapi/resources/test_handlers.py b/opnfv_testapi/resources/test_handlers.py index 5ecb176..7ab20dc 100644 --- a/opnfv_testapi/resources/test_handlers.py +++ b/opnfv_testapi/resources/test_handlers.py @@ -78,10 +78,28 @@ class TestsCLHandler(GenericTestHandler): if curr_user is None: raises.Unauthorized(message.no_auth()) + review = self.request.query_arguments.pop('review', None) query = yield self.set_query() - yield self._list(query=query, **limitations) + if review: + yield self._list(query=query, res_op=self.check_review, + **limitations) + else: + yield self._list(query=query, **limitations) logging.debug('list end') + @gen.coroutine + def check_review(self, data, *args): + current_user = self.get_secure_cookie(auth_const.OPENID) + for test in data: + query = {'reviewer_openid': current_user, 'test_id': test['id']} + ret = yield dbapi.db_find_one('reviews', query) + if ret: + test['voted'] = 'true' + else: + test['voted'] = 'false' + + raise gen.Return({self.table: data}) + @swagger.operation(nickname="createTest") @web.asynchronous def post(self): @@ -111,6 +129,8 @@ class TestsCLHandler(GenericTestHandler): self.finish_request({'code': '403', 'msg': msg}) return + if self.is_onap: + self.json_args['is_onap'] = 'true' self._create(miss_fields=miss_fields, carriers=carriers) @@ -149,7 +169,6 @@ class TestsGURHandler(GenericTestHandler): raise gen.Return('API response validation enabled') @swagger.operation(nickname="deleteTestById") - @web.asynchronous @gen.coroutine def delete(self, test_id): curr_user = self.get_secure_cookie(auth_const.OPENID) @@ -161,6 +180,9 @@ class TestsGURHandler(GenericTestHandler): raises.NotFound(message.not_found(self.table, query)) if curr_user == test_data['owner'] or \ curr_user_role.find('administrator') != -1: + yield dbapi.db_delete('applications', + {'test_id': test_data['id']}) + yield dbapi.db_delete('reviews', {'test_id': test_data['id']}) self._delete(query=query) else: raises.Forbidden(message.no_auth()) @@ -198,7 +220,7 @@ class TestsGURHandler(GenericTestHandler): if query and table: data = yield dbapi.db_find_one(table, query) if data: - raise gen.Return((True, 'Data alreay exists. %s' % (query), + raise gen.Return((True, 'Data already exists. %s' % (query), data.get("openid"))) raise gen.Return((False, 'Data does not exist. %s' % (query), None)) @@ -255,9 +277,9 @@ class TestsGURHandler(GenericTestHandler): return if item == "status": - if value in {'approved', 'not approved'}: + if value == 'verified': if test['status'] == 'private': - msg = 'Not allowed to approve/not approve' + msg = 'Not allowed to verify' self.finish_request({'code': 403, 'msg': msg}) return @@ -272,7 +294,7 @@ class TestsGURHandler(GenericTestHandler): self.finish_request({'code': 403, 'msg': msg}) return - if not test['sut_label']: + if not self.is_onap and not test['sut_label']: msg = 'Please fill out SUT version before submission' self.finish_request({'code': 403, 'msg': msg}) return @@ -284,8 +306,7 @@ class TestsGURHandler(GenericTestHandler): 'id': test['id'], '$or': [ {'status': 'review'}, - {'status': 'approved'}, - {'status': 'not approved'} + {'status': 'verified'} ] } record = yield dbapi.db_find_one("tests", test_query) @@ -311,12 +332,12 @@ class TestsGURHandler(GenericTestHandler): logging.debug('check review') query['user_id'] = user data = yield dbapi.db_find_one('applications', query) - if not data: - logging.debug('not found') + if data: + logging.debug('results are bound to an application') raise gen.Return((False, message.no_auth())) - if value == "approve" or value == "not approved": - logging.debug('check approve') - query['role'] = {"$regex": ".*reviewer.*"} + if value == "verified": + logging.debug('check verify') + query['role'] = {"$regex": ".*administrator.*"} query['openid'] = user data = yield dbapi.db_find_one('users', query) if not data: diff --git a/opnfv_testapi/router/url_mappings.py b/opnfv_testapi/router/url_mappings.py index 1eb74ad..65c8480 100644 --- a/opnfv_testapi/router/url_mappings.py +++ b/opnfv_testapi/router/url_mappings.py @@ -13,6 +13,7 @@ from opnfv_testapi.resources import test_handlers from opnfv_testapi.resources import application_handlers from opnfv_testapi.resources import pod_handlers from opnfv_testapi.resources import project_handlers +from opnfv_testapi.resources import review_handlers from opnfv_testapi.resources import scenario_handlers from opnfv_testapi.resources import sut_handlers from opnfv_testapi.resources import testcase_handlers @@ -57,3 +58,17 @@ mappings = [ (r'/api/v1/profile', user.ProfileHandler), ] + +onap_mappings = [ + (r'/api/v1/onap/results', result_handlers.ResultsCLHandler, + dict(is_onap=True)), + (r'/api/v1/onap/results/upload', result_handlers.ResultsUploadHandler, + dict(is_onap=True)), + (r'/api/v1/onap/tests', test_handlers.TestsCLHandler, + dict(is_onap=True)), + (r"/api/v1/onap/tests/([^/]+)", test_handlers.TestsGURHandler, + dict(is_onap=True)), + (r'/api/v1/onap/cvp/applications', + application_handlers.ApplicationsCLHandler, dict(is_onap=True)), + (r'/api/v1/onap/reviews', review_handlers.ReviewsCLHandler), +] diff --git a/opnfv_testapi/tornado_swagger/swagger.py b/opnfv_testapi/tornado_swagger/swagger.py index 83f389a..9afdb17 100644 --- a/opnfv_testapi/tornado_swagger/swagger.py +++ b/opnfv_testapi/tornado_swagger/swagger.py @@ -255,7 +255,8 @@ class operation(DocParser): def _parse_args(self, func): argspec = inspect.getargspec(func) - argspec.args.remove("self") + if 'self' in argspec.args: + argspec.args.remove('self') defaults = [] if argspec.defaults: diff --git a/opnfv_testapi/ui/auth/sign.py b/opnfv_testapi/ui/auth/sign.py index dbb40ed..028816a 100644 --- a/opnfv_testapi/ui/auth/sign.py +++ b/opnfv_testapi/ui/auth/sign.py @@ -45,7 +45,8 @@ class SigninHandler(base.BaseHandler): renew=False, extra_login_params=False, server_url=CONF.lfid_url, - service_url=CONF.lfid_return_url + service_url='http://{0}/{1}'.format(self.request.host, + CONF.lfid_return_url) ) redirect_url = client.get_login_url() self.redirect(url=redirect_url, permanent=False) @@ -154,7 +155,8 @@ class SigninReturnCasHandler(base.BaseHandler): renew=False, extra_login_params=False, server_url=CONF.lfid_url, - service_url=CONF.lfid_return_url + service_url='http://{0}/{1}'.format(self.request.host, + CONF.lfid_return_url) ) user, attrs, _ = client.verify_ticket(ticket) logging.debug("user:%s", user) @@ -180,7 +182,7 @@ class SigninReturnCasHandler(base.BaseHandler): self.set_secure_cookie(const.ROLE, role) self.set_secure_cookie('ticket', ticket) - self.redirect("/") + self.redirect('http://{0}'.format(self.request.host)) class SigninReturnJiraHandler(base.BaseHandler): @@ -275,7 +277,12 @@ class SignoutHandler(base.BaseHandler): renew=False, extra_login_params=False, server_url=CONF.lfid_url, - service_url=CONF.lfid_return_url + service_url='http://{0}/{1}'.format(self.request.host, + CONF.lfid_return_url) ) - url = client.get_logout_url(CONF.ui_url) + + self.clear_cookie('ticket') + self.clear_cookie('signin_type') + + url = client.get_logout_url('http://{0}'.format(self.request.host)) self.redirect(url) diff --git a/opnfv_testapi/ui/auth/user.py b/opnfv_testapi/ui/auth/user.py index a695da4..5ac6f43 100644 --- a/opnfv_testapi/ui/auth/user.py +++ b/opnfv_testapi/ui/auth/user.py @@ -10,12 +10,19 @@ from tornado import gen from tornado import web +from opnfv_testapi.common import message from opnfv_testapi.common import raises from opnfv_testapi.db import api as dbapi +from opnfv_testapi.resources import models from opnfv_testapi.ui.auth import base +from opnfv_testapi.ui.auth import constants as auth_const class ProfileHandler(base.BaseHandler): + def __init__(self, application, request, **kwargs): + super(ProfileHandler, self).__init__(application, request, **kwargs) + self.table_cls = User + @web.asynchronous @gen.coroutine def get(self): @@ -28,8 +35,51 @@ class ProfileHandler(base.BaseHandler): "email": user.get('email'), "fullname": user.get('fullname'), "role": user.get('role', 'user'), - "type": self.get_secure_cookie('signin_type') + "type": self.get_secure_cookie('signin_type'), + "companyName": user.get('companyName'), + "companyWebsite": user.get('companyWebsite'), + "primaryContactName": user.get('primaryContactName'), + "primaryBusinessEmail": user.get('primaryBusinessEmail'), + "primaryPostalAddress": user.get('primaryPostalAddress'), + "primaryPhoneNumber": user.get('primaryPhoneNumber') + }) except Exception: pass raises.Unauthorized('Unauthorized') + + @gen.coroutine + def put(self): + db_keys = [] + openid = self.get_secure_cookie(auth_const.OPENID) + + if openid: + query = {'openid': openid} + user = yield dbapi.db_find_one(self.table, query) + if not user: + raises.NotFound(message.not_found(self.table, query)) + + self._update(query=query, db_keys=db_keys) + else: + raises.Unauthorized(message.no_auth()) + + +class User(models.ModelBase): + def __init__(self, _id=None, openid=None, email=None, fullname=None, + role='user', u_type=None, companyName=None, + companyWebsite=None, primaryContactName=None, + primaryBusinessEmail=None, primaryPostalAddress=None, + primaryPhoneNumber=None): + self._id = _id + self.openid = openid + self.email = email + self.fullname = fullname + self.role = role + self.type = u_type + + self.companyName = companyName + self.companyWebsite = companyWebsite + self.primaryContactName = primaryContactName + self.primaryBusinessEmail = primaryBusinessEmail + self.primaryPostalAddress = primaryPostalAddress + self.primaryPhoneNumber = primaryPhoneNumber diff --git a/requirements.txt b/requirements.txt index b705689..07a4fe6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,3 +15,5 @@ pyjwt>=1.5.2 cryptography==2.2.2 python-cas==1.2.0 futures==3.2.0 +python-slugify==2.0.1 +Pillow==3.1.2 |