horizon-9.0.0/0000775000567000056710000000000012701407231014346 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon.egg-info/0000775000567000056710000000000012701407231017530 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon.egg-info/requires.txt0000664000567000056710000000241112701407227022133 0ustar jenkinsjenkins00000000000000pbr>=1.6 Babel>=1.3 Django<1.9,>=1.8 Pint>=0.5 django-babel>=0.4.0 django-compressor>=2.0 django-openstack-auth>=2.0.0 django-pyscss>=2.0.2 httplib2>=0.7.5 iso8601>=0.1.9 netaddr!=0.7.16,>=0.7.12 oslo.concurrency>=3.5.0 oslo.config>=3.7.0 oslo.i18n>=2.1.0 oslo.policy>=0.5.0 oslo.serialization>=1.10.0 oslo.utils>=3.5.0 pyScss>=1.3.4 python-ceilometerclient>=2.2.1 python-cinderclient>=1.3.1 python-glanceclient>=2.0.0 python-heatclient>=0.6.0 python-keystoneclient!=1.8.0,!=2.1.0,>=1.6.0 python-neutronclient!=4.1.0,>=2.6.0 python-novaclient!=2.33.0,>=2.29.0 python-swiftclient>=2.2.0 pytz>=2013.6 PyYAML>=3.1.0 six>=1.9.0 XStatic>=1.0.0 XStatic-Angular>=1.3.7 XStatic-Angular-Bootstrap>=0.11.0.2 XStatic-Angular-Gettext>=2.1.0.2 XStatic-Angular-lrdragndrop>=1.0.2.2 XStatic-Bootstrap-Datepicker>=1.3.1.0 XStatic-Bootstrap-SCSS>=3 XStatic-bootswatch>=3.3.5.3 XStatic-D3>=3.1.6.2 XStatic-Hogan>=2.0.0.2 XStatic-Font-Awesome>=4.3.0 XStatic-Jasmine>=2.1.2.0 XStatic-jQuery>=1.7.2 XStatic-JQuery-Migrate>=1.2.1.1 XStatic-JQuery.quicksearch>=2.0.3.1 XStatic-JQuery.TableSorter>=2.14.5.1 XStatic-jquery-ui>=1.10.1 XStatic-JSEncrypt>=2.0.0.2 XStatic-mdi==1.1.70.1 XStatic-Rickshaw>=1.5.0 XStatic-roboto-fontface>=0.4.3.2 XStatic-smart-table>=1.4.5.3 XStatic-Spin>=1.2.5.2 XStatic-term.js>=0.0.4 horizon-9.0.0/horizon.egg-info/dependency_links.txt0000664000567000056710000000000112701407227023603 0ustar jenkinsjenkins00000000000000 horizon-9.0.0/horizon.egg-info/not-zip-safe0000664000567000056710000000000112701407216021761 0ustar jenkinsjenkins00000000000000 horizon-9.0.0/horizon.egg-info/SOURCES.txt0000664000567000056710000043701712701407231021430 0ustar jenkinsjenkins00000000000000.eslintignore .eslintrc .mailmap .pylintrc .testr.conf AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE MANIFEST.in Makefile README.rst babel-django.cfg babel-djangojs.cfg manage.py package.json requirements.txt run_tests.sh setup.cfg setup.py test-requirements.txt test-shim.js tox.ini doc/Makefile doc/source/conf.py doc/source/contributing.rst doc/source/faq.rst doc/source/glossary.rst doc/source/index.rst doc/source/intro.rst doc/source/plugin_registry.rst doc/source/quickstart.rst doc/source/testing.rst doc/source/ref/context_processors.rst doc/source/ref/decorators.rst doc/source/ref/exceptions.rst doc/source/ref/forms.rst doc/source/ref/horizon.rst doc/source/ref/local_conf.rst doc/source/ref/middleware.rst doc/source/ref/run_tests.rst doc/source/ref/tables.rst doc/source/ref/tabs.rst doc/source/ref/test.rst doc/source/ref/workflows.rst doc/source/releases/2012_1.rst doc/source/releases/2012_2.rst doc/source/releases/2013_1.rst doc/source/releases/2013_2.rst doc/source/releases/2014_1.rst doc/source/releases/2014_2.rst doc/source/releases/2015_1.rst doc/source/releases/8.0.0.rst doc/source/topics/angularjs.rst doc/source/topics/customizing.rst doc/source/topics/deployment.rst doc/source/topics/install.rst doc/source/topics/javascript_testing.rst doc/source/topics/packaging.rst doc/source/topics/policy.rst doc/source/topics/settings.rst doc/source/topics/styling.rst doc/source/topics/table_actions.rst doc/source/topics/tables.rst doc/source/topics/testing.rst doc/source/topics/workflows.rst doc/source/tutorials/dashboard.rst doc/source/tutorials/plugin.rst horizon/__init__.py horizon/base.py horizon/context_processors.py horizon/decorators.py horizon/exceptions.py horizon/karma.conf.js horizon/loaders.py horizon/messages.py horizon/middleware.py horizon/notifications.py horizon/site_urls.py horizon/themes.py horizon/version.py horizon/views.py horizon.egg-info/PKG-INFO horizon.egg-info/SOURCES.txt horizon.egg-info/dependency_links.txt horizon.egg-info/not-zip-safe horizon.egg-info/pbr.json horizon.egg-info/requires.txt horizon.egg-info/top_level.txt horizon/browsers/__init__.py horizon/browsers/base.py horizon/browsers/breadcrumb.py horizon/browsers/views.py horizon/conf/__init__.py horizon/conf/default.py horizon/conf/dash_template/__init__.py horizon/conf/dash_template/dashboard.py.tmpl horizon/conf/dash_template/static/dash_name/js/dash_name.js horizon/conf/dash_template/static/dash_name/scss/dash_name.scss horizon/conf/dash_template/templates/dash_name/base.html horizon/conf/panel_template/__init__.py horizon/conf/panel_template/panel.py.tmpl horizon/conf/panel_template/tests.py.tmpl horizon/conf/panel_template/urls.py.tmpl horizon/conf/panel_template/views.py horizon/conf/panel_template/templates/panel_name/index.html horizon/contrib/__init__.py horizon/contrib/bootstrap_datepicker.py horizon/forms/__init__.py horizon/forms/base.py horizon/forms/fields.py horizon/forms/views.py horizon/hacking/__init__.py horizon/hacking/checks.py horizon/locale/django.pot horizon/locale/djangojs.pot horizon/locale/ca/LC_MESSAGES/django.po horizon/locale/cs/LC_MESSAGES/django.po horizon/locale/cs/LC_MESSAGES/djangojs.po horizon/locale/de/LC_MESSAGES/django.po horizon/locale/de/LC_MESSAGES/djangojs.po horizon/locale/en_AU/LC_MESSAGES/django.po horizon/locale/en_AU/LC_MESSAGES/djangojs.po horizon/locale/en_GB/LC_MESSAGES/django.po horizon/locale/en_GB/LC_MESSAGES/djangojs.po horizon/locale/es/LC_MESSAGES/django.po horizon/locale/es/LC_MESSAGES/djangojs.po horizon/locale/es_MX/LC_MESSAGES/django.po horizon/locale/fi_FI/LC_MESSAGES/django.po horizon/locale/fil/LC_MESSAGES/django.po horizon/locale/fr/LC_MESSAGES/django.po horizon/locale/fr/LC_MESSAGES/djangojs.po horizon/locale/hi/LC_MESSAGES/django.po horizon/locale/hi/LC_MESSAGES/djangojs.po horizon/locale/hu/LC_MESSAGES/django.po horizon/locale/id/LC_MESSAGES/django.po horizon/locale/it/LC_MESSAGES/django.po horizon/locale/it/LC_MESSAGES/djangojs.po horizon/locale/ja/LC_MESSAGES/django.po horizon/locale/ja/LC_MESSAGES/djangojs.po horizon/locale/ko_KR/LC_MESSAGES/django.po horizon/locale/ko_KR/LC_MESSAGES/djangojs.po horizon/locale/nl_NL/LC_MESSAGES/django.po horizon/locale/nl_NL/LC_MESSAGES/djangojs.po horizon/locale/pa_IN/LC_MESSAGES/django.po horizon/locale/pa_IN/LC_MESSAGES/djangojs.po horizon/locale/pl_PL/LC_MESSAGES/django.po horizon/locale/pl_PL/LC_MESSAGES/djangojs.po horizon/locale/pt/LC_MESSAGES/django.po horizon/locale/pt_BR/LC_MESSAGES/django.po horizon/locale/pt_BR/LC_MESSAGES/djangojs.po horizon/locale/ru/LC_MESSAGES/django.po horizon/locale/ru/LC_MESSAGES/djangojs.po horizon/locale/sl_SI/LC_MESSAGES/django.po horizon/locale/sr/LC_MESSAGES/django.po horizon/locale/sr/LC_MESSAGES/djangojs.po horizon/locale/tr_TR/LC_MESSAGES/django.po horizon/locale/tr_TR/LC_MESSAGES/djangojs.po horizon/locale/zh_CN/LC_MESSAGES/django.po horizon/locale/zh_CN/LC_MESSAGES/djangojs.po horizon/locale/zh_TW/LC_MESSAGES/django.po horizon/locale/zh_TW/LC_MESSAGES/djangojs.po horizon/management/__init__.py horizon/management/commands/__init__.py horizon/management/commands/startdash.py horizon/management/commands/startpanel.py horizon/static/auth/.eslintrc horizon/static/auth/auth.module.js horizon/static/auth/login/login-finder.directive.js horizon/static/auth/login/login.controller.js horizon/static/auth/login/login.controller.spec.js horizon/static/auth/login/login.module.js horizon/static/auth/login/login.module.spec.js horizon/static/auth/login/login.regular.mock.html horizon/static/auth/login/login.spec.js horizon/static/auth/login/login.websso.mock.html horizon/static/framework/.eslintrc horizon/static/framework/framework.module.js horizon/static/framework/framework.module.spec.js horizon/static/framework/framework.scss horizon/static/framework/conf/conf.js horizon/static/framework/conf/resource-type-registry.service.js horizon/static/framework/conf/resource-type-registry.service.spec.js horizon/static/framework/util/util.module.js horizon/static/framework/util/bind-scope/bind-scope.directive.js horizon/static/framework/util/bind-scope/bind-scope.module.js horizon/static/framework/util/bind-scope/bind-scope.spec.js horizon/static/framework/util/extensible/extensible.module.js horizon/static/framework/util/extensible/extensible.service.js horizon/static/framework/util/extensible/extensible.service.spec.js horizon/static/framework/util/file/file-reader.service.js horizon/static/framework/util/file/file-reader.service.spec.js horizon/static/framework/util/file/file.module.js horizon/static/framework/util/filters/filters.js horizon/static/framework/util/filters/filters.module.js horizon/static/framework/util/filters/filters.spec.js horizon/static/framework/util/http/http.js horizon/static/framework/util/http/http.spec.js horizon/static/framework/util/i18n/i18n.js horizon/static/framework/util/i18n/i18n.spec.js horizon/static/framework/util/promise-toggle/hz-promise-toggle.directive.js horizon/static/framework/util/promise-toggle/hz-promise-toggle.directive.mock.js horizon/static/framework/util/promise-toggle/hz-promise-toggle.directive.spec.js horizon/static/framework/util/promise-toggle/promise-toggle.module.js horizon/static/framework/util/q/q.extensions.js horizon/static/framework/util/q/q.extensions.spec.js horizon/static/framework/util/q/q.module.js horizon/static/framework/util/q/q.module.spec.js horizon/static/framework/util/tech-debt/dummy.controller.js horizon/static/framework/util/tech-debt/helper-functions.service.js horizon/static/framework/util/tech-debt/helper-functions.service.spec.js horizon/static/framework/util/tech-debt/image-file-on-change.directive.js horizon/static/framework/util/tech-debt/tech-debt.module.js horizon/static/framework/util/validators/hz-password-match.directive.js horizon/static/framework/util/validators/hz-password-match.directive.spec.js horizon/static/framework/util/validators/validate-number-max.directive.js horizon/static/framework/util/validators/validate-number-min.directive.js horizon/static/framework/util/validators/validate-unique.js horizon/static/framework/util/validators/validators.module.js horizon/static/framework/util/validators/validators.spec.js horizon/static/framework/util/workflow/workflow.module.js horizon/static/framework/util/workflow/workflow.service.js horizon/static/framework/util/workflow/workflow.spec.js horizon/static/framework/widgets/widgets.module.js horizon/static/framework/widgets/widgets.module.spec.js horizon/static/framework/widgets/widgets.scss horizon/static/framework/widgets/action-list/action-list.directive.js horizon/static/framework/widgets/action-list/action-list.module.js horizon/static/framework/widgets/action-list/action-list.scss horizon/static/framework/widgets/action-list/action-list.single-button-dropdown.mock.html horizon/static/framework/widgets/action-list/action-list.spec.js horizon/static/framework/widgets/action-list/action-list.split-botton-dropdown.mock.html horizon/static/framework/widgets/action-list/action.directive.js horizon/static/framework/widgets/action-list/action.html horizon/static/framework/widgets/action-list/actions-batch.template.html horizon/static/framework/widgets/action-list/actions-create.template.html horizon/static/framework/widgets/action-list/actions-danger.template.html horizon/static/framework/widgets/action-list/actions-delete-selected.template.html horizon/static/framework/widgets/action-list/actions-delete.template.html horizon/static/framework/widgets/action-list/actions-link.template.html horizon/static/framework/widgets/action-list/actions-row.template.html horizon/static/framework/widgets/action-list/actions.batch.mock.html horizon/static/framework/widgets/action-list/actions.controller.js horizon/static/framework/widgets/action-list/actions.custom.mock.html horizon/static/framework/widgets/action-list/actions.custom2.mock.html horizon/static/framework/widgets/action-list/actions.directive.js horizon/static/framework/widgets/action-list/actions.directive.spec.js horizon/static/framework/widgets/action-list/actions.row.mock.html horizon/static/framework/widgets/action-list/actions.service.js horizon/static/framework/widgets/action-list/button-group.mock.html horizon/static/framework/widgets/action-list/button-tooltip.directive.js horizon/static/framework/widgets/action-list/button-tooltip.row-warning.service.js horizon/static/framework/widgets/action-list/link.html horizon/static/framework/widgets/action-list/menu-item.html horizon/static/framework/widgets/action-list/menu.directive.js horizon/static/framework/widgets/action-list/menu.html horizon/static/framework/widgets/action-list/single-button.html horizon/static/framework/widgets/action-list/split-button.html horizon/static/framework/widgets/action-list/split-link.html horizon/static/framework/widgets/action-list/warning-tooltip.html horizon/static/framework/widgets/charts/chart-tooltip.directive.js horizon/static/framework/widgets/charts/chart-tooltip.html horizon/static/framework/widgets/charts/chart-tooltip.scss horizon/static/framework/widgets/charts/chart-tooltip.spec.js horizon/static/framework/widgets/charts/charts.module.js horizon/static/framework/widgets/charts/charts.spec.js horizon/static/framework/widgets/charts/pie-chart.directive.js horizon/static/framework/widgets/charts/pie-chart.html horizon/static/framework/widgets/charts/pie-chart.scss horizon/static/framework/widgets/charts/pie-chart.spec.js horizon/static/framework/widgets/headers/headers.module.js horizon/static/framework/widgets/headers/headers.module.spec.js horizon/static/framework/widgets/headers/headers.scss horizon/static/framework/widgets/headers/hz-page-header.directive.js horizon/static/framework/widgets/headers/hz-page-header.directive.spec.js horizon/static/framework/widgets/headers/hz-page-header.html horizon/static/framework/widgets/help-panel/help-panel.directive.js horizon/static/framework/widgets/help-panel/help-panel.directive.spec.js horizon/static/framework/widgets/help-panel/help-panel.html horizon/static/framework/widgets/help-panel/help-panel.module.js horizon/static/framework/widgets/help-panel/help-panel.module.spec.js horizon/static/framework/widgets/load-edit/load-edit.directive.js horizon/static/framework/widgets/load-edit/load-edit.directive.spec.js horizon/static/framework/widgets/load-edit/load-edit.html horizon/static/framework/widgets/load-edit/load-edit.module.js horizon/static/framework/widgets/magic-search/hz-magic-search-bar.directive.js horizon/static/framework/widgets/magic-search/hz-magic-search-bar.html horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js horizon/static/framework/widgets/magic-search/magic-search.controller.js horizon/static/framework/widgets/magic-search/magic-search.controller.spec.js horizon/static/framework/widgets/magic-search/magic-search.directive.js horizon/static/framework/widgets/magic-search/magic-search.html horizon/static/framework/widgets/magic-search/magic-search.module.js horizon/static/framework/widgets/magic-search/magic-search.scss horizon/static/framework/widgets/magic-search/magic-search.service.js horizon/static/framework/widgets/magic-search/magic-search.service.spec.js horizon/static/framework/widgets/magic-search/st-magic-search.directive.js horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js horizon/static/framework/widgets/metadata/metadata.module.js horizon/static/framework/widgets/metadata/metadata.scss horizon/static/framework/widgets/metadata/display/display.module.js horizon/static/framework/widgets/metadata/display/display.scss horizon/static/framework/widgets/metadata/display/display.spec.js horizon/static/framework/widgets/metadata/display/metadata-display.controller.js horizon/static/framework/widgets/metadata/display/metadata-display.directive.js horizon/static/framework/widgets/metadata/display/metadata-display.html horizon/static/framework/widgets/metadata/tree/metadata-tree-item.controller.js horizon/static/framework/widgets/metadata/tree/metadata-tree-item.controller.spec.js horizon/static/framework/widgets/metadata/tree/metadata-tree-item.directive.js horizon/static/framework/widgets/metadata/tree/metadata-tree-item.html horizon/static/framework/widgets/metadata/tree/metadata-tree.controller.js horizon/static/framework/widgets/metadata/tree/metadata-tree.directive.js horizon/static/framework/widgets/metadata/tree/metadata-tree.html horizon/static/framework/widgets/metadata/tree/tree.module.js horizon/static/framework/widgets/metadata/tree/tree.scss horizon/static/framework/widgets/metadata/tree/tree.service.js horizon/static/framework/widgets/metadata/tree/tree.spec.js horizon/static/framework/widgets/modal/delete-modal.service.js horizon/static/framework/widgets/modal/delete-modal.service.spec.js horizon/static/framework/widgets/modal/modal.module.js horizon/static/framework/widgets/modal/simple-modal.controller.js horizon/static/framework/widgets/modal/simple-modal.html horizon/static/framework/widgets/modal/simple-modal.service.js horizon/static/framework/widgets/modal/simple-modal.spec.js horizon/static/framework/widgets/modal/wizard-modal.service.js horizon/static/framework/widgets/modal/wizard-modal.service.spec.js horizon/static/framework/widgets/modal/wizard.controller.js horizon/static/framework/widgets/modal/wizard.controller.spec.js horizon/static/framework/widgets/modal-wait-spinner/modal-wait-spinner.directive.js horizon/static/framework/widgets/modal-wait-spinner/modal-wait-spinner.module.js horizon/static/framework/widgets/modal-wait-spinner/modal-wait-spinner.scss horizon/static/framework/widgets/modal-wait-spinner/modal-wait-spinner.service.js horizon/static/framework/widgets/modal-wait-spinner/modal-wait-spinner.spec.js horizon/static/framework/widgets/table/hz-expand-detail.directive.js horizon/static/framework/widgets/table/hz-no-items.directive.js horizon/static/framework/widgets/table/hz-no-items.directive.spec.js horizon/static/framework/widgets/table/hz-no-items.html horizon/static/framework/widgets/table/hz-search-bar.directive.js horizon/static/framework/widgets/table/hz-select-all.directive.js horizon/static/framework/widgets/table/hz-select.directive.js horizon/static/framework/widgets/table/hz-table-footer.directive.js horizon/static/framework/widgets/table/hz-table-footer.html horizon/static/framework/widgets/table/hz-table.directive.js horizon/static/framework/widgets/table/no-items.mock.html horizon/static/framework/widgets/table/search-bar.html horizon/static/framework/widgets/table/search-bar.spec.js horizon/static/framework/widgets/table/st-table.mock.html horizon/static/framework/widgets/table/table.controller.js horizon/static/framework/widgets/table/table.mock.html horizon/static/framework/widgets/table/table.module.js horizon/static/framework/widgets/table/table.scss horizon/static/framework/widgets/table/table.spec.js horizon/static/framework/widgets/toast/toast.directive.js horizon/static/framework/widgets/toast/toast.html horizon/static/framework/widgets/toast/toast.module.js horizon/static/framework/widgets/toast/toast.service.js horizon/static/framework/widgets/toast/toast.spec.js horizon/static/framework/widgets/transfer-table/transfer-table.basic.mock.html horizon/static/framework/widgets/transfer-table/transfer-table.clone.mock.html horizon/static/framework/widgets/transfer-table/transfer-table.controller.js horizon/static/framework/widgets/transfer-table/transfer-table.controller.spec.js horizon/static/framework/widgets/transfer-table/transfer-table.directive.js horizon/static/framework/widgets/transfer-table/transfer-table.directive.spec.js horizon/static/framework/widgets/transfer-table/transfer-table.example.html horizon/static/framework/widgets/transfer-table/transfer-table.html horizon/static/framework/widgets/transfer-table/transfer-table.max-1.mock.html horizon/static/framework/widgets/transfer-table/transfer-table.max-2.mock.html horizon/static/framework/widgets/transfer-table/transfer-table.module.js horizon/static/framework/widgets/transfer-table/transfer-table.module.spec.js horizon/static/framework/widgets/wizard/modal-container.controller.js horizon/static/framework/widgets/wizard/wizard.controller.js horizon/static/framework/widgets/wizard/wizard.directive.js horizon/static/framework/widgets/wizard/wizard.html horizon/static/framework/widgets/wizard/wizard.module.js horizon/static/framework/widgets/wizard/wizard.spec.js horizon/static/horizon/.eslintrc horizon/static/horizon/js/horizon.communication.js horizon/static/horizon/js/horizon.d3barchart.js horizon/static/horizon/js/horizon.d3linechart.js horizon/static/horizon/js/horizon.d3piechart.js horizon/static/horizon/js/horizon.datepickers.js horizon/static/horizon/js/horizon.firewalls.js horizon/static/horizon/js/horizon.forms.js horizon/static/horizon/js/horizon.formset_table.js horizon/static/horizon/js/horizon.heattop.js horizon/static/horizon/js/horizon.instances.js horizon/static/horizon/js/horizon.js horizon/static/horizon/js/horizon.membership.js horizon/static/horizon/js/horizon.messages.js horizon/static/horizon/js/horizon.metering.js horizon/static/horizon/js/horizon.modals.js horizon/static/horizon/js/horizon.networktopology.js horizon/static/horizon/js/horizon.quota.js horizon/static/horizon/js/horizon.selenium.js horizon/static/horizon/js/horizon.string.js horizon/static/horizon/js/horizon.tables.js horizon/static/horizon/js/horizon.tables_inline_edit.js horizon/static/horizon/js/horizon.tabs.js horizon/static/horizon/js/horizon.templates.js horizon/static/horizon/js/horizon.users.js horizon/static/horizon/js/horizon.volumes.js horizon/static/horizon/js/angular/directives/serialConsole.js horizon/static/horizon/lib/jquery/jquery.bootstrap.wizard.js horizon/static/horizon/tests/jasmine/instances.legacy-spec.js horizon/static/horizon/tests/jasmine/messages.legacy-spec.js horizon/static/horizon/tests/jasmine/modals.legacy-spec.js horizon/static/horizon/tests/jasmine/quota.legacy-spec.js horizon/static/horizon/tests/jasmine/string.legacy-spec.js horizon/static/horizon/tests/jasmine/tables.legacy-spec.js horizon/static/horizon/tests/jasmine/templates.legacy-spec.js horizon/tables/__init__.py horizon/tables/actions.py horizon/tables/base.py horizon/tables/formset.py horizon/tables/views.py horizon/tabs/__init__.py horizon/tabs/base.py horizon/tabs/views.py horizon/templates/auth/_description.html horizon/templates/auth/_login.html horizon/templates/auth/_login_form.html horizon/templates/auth/_login_modal.html horizon/templates/auth/_login_page.html horizon/templates/auth/_splash.html horizon/templates/auth/login.html horizon/templates/bootstrap/progress_bar.html horizon/templates/horizon/_messages.html horizon/templates/horizon/_nav_list.html horizon/templates/horizon/_script_i18n.html horizon/templates/horizon/_sidebar.html horizon/templates/horizon/_subnav_list.html horizon/templates/horizon/client_side/_alert_message.html horizon/templates/horizon/client_side/_loading.html horizon/templates/horizon/client_side/_membership.html horizon/templates/horizon/client_side/_modal.html horizon/templates/horizon/client_side/_script_loader.html horizon/templates/horizon/client_side/_table_row.html horizon/templates/horizon/client_side/template.html horizon/templates/horizon/client_side/templates.html horizon/templates/horizon/common/_breadcrumb.html horizon/templates/horizon/common/_breadcrumb_nav.html horizon/templates/horizon/common/_data_table.html horizon/templates/horizon/common/_data_table_action.html horizon/templates/horizon/common/_data_table_cell.html horizon/templates/horizon/common/_data_table_row.html horizon/templates/horizon/common/_data_table_row_action_row.html horizon/templates/horizon/common/_data_table_row_actions_dropdown.html horizon/templates/horizon/common/_data_table_row_actions_row.html horizon/templates/horizon/common/_data_table_table_actions.html horizon/templates/horizon/common/_detail.html horizon/templates/horizon/common/_detail_table.html horizon/templates/horizon/common/_domain_page_header.html horizon/templates/horizon/common/_form_errors.html horizon/templates/horizon/common/_form_field.html horizon/templates/horizon/common/_form_field_required.html horizon/templates/horizon/common/_form_fields.html horizon/templates/horizon/common/_formset_table.html horizon/templates/horizon/common/_formset_table_row.html horizon/templates/horizon/common/_horizontal_field.html horizon/templates/horizon/common/_horizontal_fields.html horizon/templates/horizon/common/_limit_summary.html horizon/templates/horizon/common/_modal.html horizon/templates/horizon/common/_modal_form.html horizon/templates/horizon/common/_modal_form_add_members.html horizon/templates/horizon/common/_page_header.html horizon/templates/horizon/common/_region_selector.html horizon/templates/horizon/common/_resource_browser.html horizon/templates/horizon/common/_sidebar.html horizon/templates/horizon/common/_sidebar_module.html horizon/templates/horizon/common/_tab_group.html horizon/templates/horizon/common/_usage_summary.html horizon/templates/horizon/common/_workflow.html horizon/templates/horizon/common/_workflow_base.html horizon/templates/horizon/common/_workflow_step.html horizon/templates/horizon/common/_workflow_step_update_members.html horizon/templates/horizon/common/fields/_themable_checkbox.html horizon/templates/horizon/jasmine/index.html horizon/templates/horizon/jasmine/jasmine.html horizon/templates/horizon/jasmine/jasmine_legacy.html horizon/templatetags/__init__.py horizon/templatetags/bootstrap.py horizon/templatetags/branding.py horizon/templatetags/breadcrumb_nav.py horizon/templatetags/form_helpers.py horizon/templatetags/horizon.py horizon/templatetags/parse_date.py horizon/templatetags/shellfilter.py horizon/templatetags/sizeformat.py horizon/templatetags/truncate_filter.py horizon/test/__init__.py horizon/test/firefox_binary.py horizon/test/helpers.py horizon/test/settings.py horizon/test/test_hacking.py horizon/test/urls.py horizon/test/utils.py horizon/test/webdriver.py horizon/test/customization/__init__.py horizon/test/customization/cust_test1.py horizon/test/customization/cust_test2.py horizon/test/dummy_auth/__init__.py horizon/test/dummy_auth/backend.py horizon/test/jasmine/__init__.py horizon/test/jasmine/jasmine.py horizon/test/messages/test_info.json horizon/test/messages/test_invalid.json horizon/test/messages/test_warning.json horizon/test/templates/404.html horizon/test/templates/_tab.html horizon/test/templates/base-sidebar.html horizon/test/templates/base.html horizon/test/templates/tab_group.html horizon/test/templates/workflow.html horizon/test/templates/registration/login.html horizon/test/test_dashboards/__init__.py horizon/test/test_dashboards/cats/__init__.py horizon/test/test_dashboards/cats/dashboard.py horizon/test/test_dashboards/cats/kittens/__init__.py horizon/test/test_dashboards/cats/kittens/panel.py horizon/test/test_dashboards/cats/kittens/urls.py horizon/test/test_dashboards/cats/kittens/views.py horizon/test/test_dashboards/cats/kittens/templates/kittens/index.html horizon/test/test_dashboards/cats/static/cats/js/cats.js horizon/test/test_dashboards/cats/static/cats/scss/cats.scss horizon/test/test_dashboards/cats/templates/cats/base.html horizon/test/test_dashboards/cats/tigers/__init__.py horizon/test/test_dashboards/cats/tigers/panel.py horizon/test/test_dashboards/cats/tigers/urls.py horizon/test/test_dashboards/cats/tigers/views.py horizon/test/test_dashboards/cats/tigers/templates/tigers/index.html horizon/test/test_dashboards/dogs/__init__.py horizon/test/test_dashboards/dogs/dashboard.py horizon/test/test_dashboards/dogs/puppies/__init__.py horizon/test/test_dashboards/dogs/puppies/panel.py horizon/test/test_dashboards/dogs/puppies/tables.py horizon/test/test_dashboards/dogs/puppies/tabs.py horizon/test/test_dashboards/dogs/puppies/urls.py horizon/test/test_dashboards/dogs/puppies/views.py horizon/test/test_dashboards/dogs/puppies/templates/puppies/index.html horizon/test/test_dashboards/dogs/puppies/templates/puppies/two_tabs.html horizon/test/test_dashboards/dogs/static/dogs/js/dogs.js horizon/test/test_dashboards/dogs/static/dogs/scss/dogs.scss horizon/test/test_dashboards/dogs/templates/dogs/base.html horizon/test/tests/__init__.py horizon/test/tests/babel_extract_angular.py horizon/test/tests/base.py horizon/test/tests/exceptions.py horizon/test/tests/forms.py horizon/test/tests/messages.py horizon/test/tests/middleware.py horizon/test/tests/notifications.py horizon/test/tests/selenium_tests.py horizon/test/tests/tables.py horizon/test/tests/tabs.py horizon/test/tests/templatetags.py horizon/test/tests/test_commands.py horizon/test/tests/test_file_discovery.py horizon/test/tests/utils.py horizon/test/tests/views.py horizon/test/tests/workflows.py horizon/utils/__init__.py horizon/utils/babel_extract_angular.py horizon/utils/csvbase.py horizon/utils/file_discovery.py horizon/utils/filters.py horizon/utils/functions.py horizon/utils/html.py horizon/utils/lazy_encoder.py horizon/utils/memoized.py horizon/utils/scss_filter.py horizon/utils/secret_key.py horizon/utils/units.py horizon/utils/validators.py horizon/workflows/__init__.py horizon/workflows/base.py horizon/workflows/views.py openstack_dashboard/.eslintrc openstack_dashboard/__init__.py openstack_dashboard/context_processors.py openstack_dashboard/exceptions.py openstack_dashboard/hooks.py openstack_dashboard/karma.conf.js openstack_dashboard/policy.py openstack_dashboard/settings.py openstack_dashboard/static_settings.py openstack_dashboard/theme_settings.py openstack_dashboard/urls.py openstack_dashboard/views.py openstack_dashboard/api/__init__.py openstack_dashboard/api/base.py openstack_dashboard/api/ceilometer.py openstack_dashboard/api/cinder.py openstack_dashboard/api/fwaas.py openstack_dashboard/api/glance.py openstack_dashboard/api/heat.py openstack_dashboard/api/keystone.py openstack_dashboard/api/lbaas.py openstack_dashboard/api/network.py openstack_dashboard/api/network_base.py openstack_dashboard/api/neutron.py openstack_dashboard/api/nova.py openstack_dashboard/api/swift.py openstack_dashboard/api/vpn.py openstack_dashboard/api/rest/__init__.py openstack_dashboard/api/rest/cinder.py openstack_dashboard/api/rest/config.py openstack_dashboard/api/rest/glance.py openstack_dashboard/api/rest/heat.py openstack_dashboard/api/rest/json_encoder.py openstack_dashboard/api/rest/keystone.py openstack_dashboard/api/rest/network.py openstack_dashboard/api/rest/neutron.py openstack_dashboard/api/rest/nova.py openstack_dashboard/api/rest/policy.py openstack_dashboard/api/rest/swift.py openstack_dashboard/api/rest/urls.py openstack_dashboard/api/rest/utils.py openstack_dashboard/conf/ceilometer_policy.json openstack_dashboard/conf/cinder_policy.json openstack_dashboard/conf/glance_policy.json openstack_dashboard/conf/heat_policy.json openstack_dashboard/conf/keystone_policy.json openstack_dashboard/conf/neutron_policy.json openstack_dashboard/conf/nova_policy.json openstack_dashboard/contrib/__init__.py openstack_dashboard/contrib/developer/__init__.py openstack_dashboard/contrib/developer/dashboard.py openstack_dashboard/contrib/developer/tests.py openstack_dashboard/contrib/developer/static/dashboard/developer/developer.module.js openstack_dashboard/contrib/developer/static/dashboard/developer/developer.module.spec.js openstack_dashboard/contrib/developer/static/dashboard/developer/developer.scss openstack_dashboard/contrib/developer/static/dashboard/developer/theme-preview/theme-preview.directive.js openstack_dashboard/contrib/developer/static/dashboard/developer/theme-preview/theme-preview.html openstack_dashboard/contrib/developer/static/dashboard/developer/theme-preview/theme-preview.module.js openstack_dashboard/contrib/developer/static/dashboard/developer/theme-preview/theme-preview.module.spec.js openstack_dashboard/contrib/developer/static/dashboard/developer/theme-preview/theme-preview.scss openstack_dashboard/contrib/developer/theme_preview/__init__.py openstack_dashboard/contrib/developer/theme_preview/panel.py openstack_dashboard/contrib/developer/theme_preview/tests.py openstack_dashboard/contrib/developer/theme_preview/urls.py openstack_dashboard/contrib/developer/theme_preview/views.py openstack_dashboard/contrib/developer/theme_preview/templates/theme_preview/index.html openstack_dashboard/dashboards/__init__.py openstack_dashboard/dashboards/admin/__init__.py openstack_dashboard/dashboards/admin/dashboard.py openstack_dashboard/dashboards/admin/aggregates/__init__.py openstack_dashboard/dashboards/admin/aggregates/constants.py openstack_dashboard/dashboards/admin/aggregates/forms.py openstack_dashboard/dashboards/admin/aggregates/panel.py openstack_dashboard/dashboards/admin/aggregates/tables.py openstack_dashboard/dashboards/admin/aggregates/tests.py openstack_dashboard/dashboards/admin/aggregates/urls.py openstack_dashboard/dashboards/admin/aggregates/views.py openstack_dashboard/dashboards/admin/aggregates/workflows.py openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/_update.html openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/create.html openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/index.html openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/manage_hosts.html openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update.html openstack_dashboard/dashboards/admin/defaults/__init__.py openstack_dashboard/dashboards/admin/defaults/panel.py openstack_dashboard/dashboards/admin/defaults/tables.py openstack_dashboard/dashboards/admin/defaults/tabs.py openstack_dashboard/dashboards/admin/defaults/tests.py openstack_dashboard/dashboards/admin/defaults/urls.py openstack_dashboard/dashboards/admin/defaults/views.py openstack_dashboard/dashboards/admin/defaults/workflows.py openstack_dashboard/dashboards/admin/defaults/templates/defaults/index.html openstack_dashboard/dashboards/admin/flavors/__init__.py openstack_dashboard/dashboards/admin/flavors/constants.py openstack_dashboard/dashboards/admin/flavors/panel.py openstack_dashboard/dashboards/admin/flavors/tables.py openstack_dashboard/dashboards/admin/flavors/tests.py openstack_dashboard/dashboards/admin/flavors/urls.py openstack_dashboard/dashboards/admin/flavors/views.py openstack_dashboard/dashboards/admin/flavors/workflows.py openstack_dashboard/dashboards/admin/flavors/templates/flavors/create.html openstack_dashboard/dashboards/admin/flavors/templates/flavors/index.html openstack_dashboard/dashboards/admin/flavors/templates/flavors/update.html openstack_dashboard/dashboards/admin/hypervisors/__init__.py openstack_dashboard/dashboards/admin/hypervisors/panel.py openstack_dashboard/dashboards/admin/hypervisors/tables.py openstack_dashboard/dashboards/admin/hypervisors/tabs.py openstack_dashboard/dashboards/admin/hypervisors/tests.py openstack_dashboard/dashboards/admin/hypervisors/urls.py openstack_dashboard/dashboards/admin/hypervisors/views.py openstack_dashboard/dashboards/admin/hypervisors/compute/__init__.py openstack_dashboard/dashboards/admin/hypervisors/compute/forms.py openstack_dashboard/dashboards/admin/hypervisors/compute/tables.py openstack_dashboard/dashboards/admin/hypervisors/compute/tabs.py openstack_dashboard/dashboards/admin/hypervisors/compute/tests.py openstack_dashboard/dashboards/admin/hypervisors/compute/urls.py openstack_dashboard/dashboards/admin/hypervisors/compute/views.py openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/detail.html openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/index.html openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/_disable_service.html openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/_evacuate_host.html openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/_migrate_host.html openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/disable_service.html openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/evacuate_host.html openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/migrate_host.html openstack_dashboard/dashboards/admin/images/__init__.py openstack_dashboard/dashboards/admin/images/forms.py openstack_dashboard/dashboards/admin/images/panel.py openstack_dashboard/dashboards/admin/images/tables.py openstack_dashboard/dashboards/admin/images/tests.py openstack_dashboard/dashboards/admin/images/urls.py openstack_dashboard/dashboards/admin/images/views.py openstack_dashboard/dashboards/admin/images/templates/images/_create.html openstack_dashboard/dashboards/admin/images/templates/images/_update.html openstack_dashboard/dashboards/admin/images/templates/images/create.html openstack_dashboard/dashboards/admin/images/templates/images/index.html openstack_dashboard/dashboards/admin/images/templates/images/update.html openstack_dashboard/dashboards/admin/info/__init__.py openstack_dashboard/dashboards/admin/info/constants.py openstack_dashboard/dashboards/admin/info/panel.py openstack_dashboard/dashboards/admin/info/tables.py openstack_dashboard/dashboards/admin/info/tabs.py openstack_dashboard/dashboards/admin/info/tests.py openstack_dashboard/dashboards/admin/info/urls.py openstack_dashboard/dashboards/admin/info/views.py openstack_dashboard/dashboards/admin/info/templates/info/_cell_status.html openstack_dashboard/dashboards/admin/info/templates/info/index.html openstack_dashboard/dashboards/admin/instances/__init__.py openstack_dashboard/dashboards/admin/instances/forms.py openstack_dashboard/dashboards/admin/instances/panel.py openstack_dashboard/dashboards/admin/instances/tables.py openstack_dashboard/dashboards/admin/instances/tests.py openstack_dashboard/dashboards/admin/instances/urls.py openstack_dashboard/dashboards/admin/instances/views.py openstack_dashboard/dashboards/admin/instances/templates/instances/_live_migrate.html openstack_dashboard/dashboards/admin/instances/templates/instances/index.html openstack_dashboard/dashboards/admin/instances/templates/instances/live_migrate.html openstack_dashboard/dashboards/admin/metadata_defs/__init__.py openstack_dashboard/dashboards/admin/metadata_defs/constants.py openstack_dashboard/dashboards/admin/metadata_defs/forms.py openstack_dashboard/dashboards/admin/metadata_defs/panel.py openstack_dashboard/dashboards/admin/metadata_defs/tables.py openstack_dashboard/dashboards/admin/metadata_defs/tabs.py openstack_dashboard/dashboards/admin/metadata_defs/tests.py openstack_dashboard/dashboards/admin/metadata_defs/urls.py openstack_dashboard/dashboards/admin/metadata_defs/views.py openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/_create.html openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/_detail_contents.html openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/_detail_overview.html openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/_resource_types.html openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/create.html openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/index.html openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/resource_types.html openstack_dashboard/dashboards/admin/metering/__init__.py openstack_dashboard/dashboards/admin/metering/forms.py openstack_dashboard/dashboards/admin/metering/panel.py openstack_dashboard/dashboards/admin/metering/tables.py openstack_dashboard/dashboards/admin/metering/tabs.py openstack_dashboard/dashboards/admin/metering/tests.py openstack_dashboard/dashboards/admin/metering/urls.py openstack_dashboard/dashboards/admin/metering/views.py openstack_dashboard/dashboards/admin/metering/templates/metering/_daily.html openstack_dashboard/dashboards/admin/metering/templates/metering/daily.html openstack_dashboard/dashboards/admin/metering/templates/metering/index.html openstack_dashboard/dashboards/admin/metering/templates/metering/stats.html openstack_dashboard/dashboards/admin/networks/__init__.py openstack_dashboard/dashboards/admin/networks/forms.py openstack_dashboard/dashboards/admin/networks/panel.py openstack_dashboard/dashboards/admin/networks/tables.py openstack_dashboard/dashboards/admin/networks/tests.py openstack_dashboard/dashboards/admin/networks/urls.py openstack_dashboard/dashboards/admin/networks/views.py openstack_dashboard/dashboards/admin/networks/agents/__init__.py openstack_dashboard/dashboards/admin/networks/agents/forms.py openstack_dashboard/dashboards/admin/networks/agents/tables.py openstack_dashboard/dashboards/admin/networks/agents/tests.py openstack_dashboard/dashboards/admin/networks/agents/views.py openstack_dashboard/dashboards/admin/networks/ports/__init__.py openstack_dashboard/dashboards/admin/networks/ports/forms.py openstack_dashboard/dashboards/admin/networks/ports/tables.py openstack_dashboard/dashboards/admin/networks/ports/tabs.py openstack_dashboard/dashboards/admin/networks/ports/tests.py openstack_dashboard/dashboards/admin/networks/ports/urls.py openstack_dashboard/dashboards/admin/networks/ports/views.py openstack_dashboard/dashboards/admin/networks/subnets/__init__.py openstack_dashboard/dashboards/admin/networks/subnets/tables.py openstack_dashboard/dashboards/admin/networks/subnets/urls.py openstack_dashboard/dashboards/admin/networks/subnets/views.py openstack_dashboard/dashboards/admin/networks/subnets/workflows.py openstack_dashboard/dashboards/admin/networks/templates/networks/_create.html openstack_dashboard/dashboards/admin/networks/templates/networks/_update.html openstack_dashboard/dashboards/admin/networks/templates/networks/create.html openstack_dashboard/dashboards/admin/networks/templates/networks/index.html openstack_dashboard/dashboards/admin/networks/templates/networks/update.html openstack_dashboard/dashboards/admin/networks/templates/networks/agents/_add.html openstack_dashboard/dashboards/admin/networks/templates/networks/agents/add.html openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_create.html openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_update.html openstack_dashboard/dashboards/admin/networks/templates/networks/ports/create.html openstack_dashboard/dashboards/admin/networks/templates/networks/ports/update.html openstack_dashboard/dashboards/admin/ngflavors/__init__.py openstack_dashboard/dashboards/admin/ngflavors/panel.py openstack_dashboard/dashboards/admin/ngflavors/urls.py openstack_dashboard/dashboards/admin/ngflavors/views.py openstack_dashboard/dashboards/admin/ngflavors/templates/ngflavors/index.html openstack_dashboard/dashboards/admin/overview/__init__.py openstack_dashboard/dashboards/admin/overview/panel.py openstack_dashboard/dashboards/admin/overview/tests.py openstack_dashboard/dashboards/admin/overview/urls.py openstack_dashboard/dashboards/admin/overview/views.py openstack_dashboard/dashboards/admin/overview/templates/overview/usage.csv openstack_dashboard/dashboards/admin/overview/templates/overview/usage.html openstack_dashboard/dashboards/admin/routers/__init__.py openstack_dashboard/dashboards/admin/routers/forms.py openstack_dashboard/dashboards/admin/routers/panel.py openstack_dashboard/dashboards/admin/routers/tables.py openstack_dashboard/dashboards/admin/routers/tabs.py openstack_dashboard/dashboards/admin/routers/tests.py openstack_dashboard/dashboards/admin/routers/urls.py openstack_dashboard/dashboards/admin/routers/views.py openstack_dashboard/dashboards/admin/routers/extensions/__init__.py openstack_dashboard/dashboards/admin/routers/extensions/extraroutes/__init__.py openstack_dashboard/dashboards/admin/routers/extensions/extraroutes/tables.py openstack_dashboard/dashboards/admin/routers/ports/__init__.py openstack_dashboard/dashboards/admin/routers/ports/tables.py openstack_dashboard/dashboards/admin/routers/ports/tabs.py openstack_dashboard/dashboards/admin/routers/ports/urls.py openstack_dashboard/dashboards/admin/routers/ports/views.py openstack_dashboard/dashboards/admin/routers/templates/routers/index.html openstack_dashboard/dashboards/admin/routers/templates/routers/update.html openstack_dashboard/dashboards/admin/static/dashboard/admin/admin.module.js openstack_dashboard/dashboards/admin/static/dashboard/admin/admin.module.spec.js openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/flavors.module.js openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/flavors.module.spec.js openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/filters/has-extras.filter.js openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/filters/has-extras.filter.spec.js openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.controller.js openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.controller.spec.js openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.html openstack_dashboard/dashboards/admin/volumes/__init__.py openstack_dashboard/dashboards/admin/volumes/panel.py openstack_dashboard/dashboards/admin/volumes/tabs.py openstack_dashboard/dashboards/admin/volumes/tests.py openstack_dashboard/dashboards/admin/volumes/urls.py openstack_dashboard/dashboards/admin/volumes/views.py openstack_dashboard/dashboards/admin/volumes/snapshots/__init__.py openstack_dashboard/dashboards/admin/volumes/snapshots/forms.py openstack_dashboard/dashboards/admin/volumes/snapshots/tables.py openstack_dashboard/dashboards/admin/volumes/snapshots/tabs.py openstack_dashboard/dashboards/admin/volumes/snapshots/tests.py openstack_dashboard/dashboards/admin/volumes/snapshots/urls.py openstack_dashboard/dashboards/admin/volumes/snapshots/views.py openstack_dashboard/dashboards/admin/volumes/templates/volumes/index.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/snapshots/_detail_overview.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/snapshots/_update_status.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/snapshots/update_status.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_associate_qos_spec.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_qos_spec.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_volume_type.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_volume_type_encryption.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_edit_qos_spec_consumer.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_update_volume_type.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_update_volume_type_encryption.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_volume_encryption_type_detail.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/associate_qos_spec.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_qos_spec.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_volume_type.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_volume_type_encryption.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/edit_qos_spec_consumer.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/update_volume_type.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/update_volume_type_encryption.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/volume_encryption_type_detail.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/volume_types_tables.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/_create.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/_edit.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/_index.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/create.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/edit.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/index.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/_create.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/_edit.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/_index.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/create.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/edit.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/index.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_manage_volume.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_migrate_volume.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_unmanage_volume.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_update_status.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/manage_volume.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/migrate_volume.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/unmanage_volume.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/update_status.html openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/volumes_tables.html openstack_dashboard/dashboards/admin/volumes/volume_types/__init__.py openstack_dashboard/dashboards/admin/volumes/volume_types/forms.py openstack_dashboard/dashboards/admin/volumes/volume_types/tables.py openstack_dashboard/dashboards/admin/volumes/volume_types/tests.py openstack_dashboard/dashboards/admin/volumes/volume_types/urls.py openstack_dashboard/dashboards/admin/volumes/volume_types/views.py openstack_dashboard/dashboards/admin/volumes/volume_types/extras/__init__.py openstack_dashboard/dashboards/admin/volumes/volume_types/extras/forms.py openstack_dashboard/dashboards/admin/volumes/volume_types/extras/tables.py openstack_dashboard/dashboards/admin/volumes/volume_types/extras/tests.py openstack_dashboard/dashboards/admin/volumes/volume_types/extras/urls.py openstack_dashboard/dashboards/admin/volumes/volume_types/extras/views.py openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/__init__.py openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/forms.py openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/tables.py openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/tests.py openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/urls.py openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/views.py openstack_dashboard/dashboards/admin/volumes/volumes/__init__.py openstack_dashboard/dashboards/admin/volumes/volumes/forms.py openstack_dashboard/dashboards/admin/volumes/volumes/tables.py openstack_dashboard/dashboards/admin/volumes/volumes/tests.py openstack_dashboard/dashboards/admin/volumes/volumes/urls.py openstack_dashboard/dashboards/admin/volumes/volumes/views.py openstack_dashboard/dashboards/identity/__init__.py openstack_dashboard/dashboards/identity/dashboard.py openstack_dashboard/dashboards/identity/domains/__init__.py openstack_dashboard/dashboards/identity/domains/constants.py openstack_dashboard/dashboards/identity/domains/panel.py openstack_dashboard/dashboards/identity/domains/tables.py openstack_dashboard/dashboards/identity/domains/tests.py openstack_dashboard/dashboards/identity/domains/urls.py openstack_dashboard/dashboards/identity/domains/views.py openstack_dashboard/dashboards/identity/domains/workflows.py openstack_dashboard/dashboards/identity/domains/templates/domains/index.html openstack_dashboard/dashboards/identity/groups/__init__.py openstack_dashboard/dashboards/identity/groups/constants.py openstack_dashboard/dashboards/identity/groups/forms.py openstack_dashboard/dashboards/identity/groups/panel.py openstack_dashboard/dashboards/identity/groups/tables.py openstack_dashboard/dashboards/identity/groups/tests.py openstack_dashboard/dashboards/identity/groups/urls.py openstack_dashboard/dashboards/identity/groups/views.py openstack_dashboard/dashboards/identity/groups/templates/groups/_add_non_member.html openstack_dashboard/dashboards/identity/groups/templates/groups/_create.html openstack_dashboard/dashboards/identity/groups/templates/groups/_update.html openstack_dashboard/dashboards/identity/groups/templates/groups/add_non_member.html openstack_dashboard/dashboards/identity/groups/templates/groups/create.html openstack_dashboard/dashboards/identity/groups/templates/groups/index.html openstack_dashboard/dashboards/identity/groups/templates/groups/manage.html openstack_dashboard/dashboards/identity/groups/templates/groups/update.html openstack_dashboard/dashboards/identity/identity_providers/__init__.py openstack_dashboard/dashboards/identity/identity_providers/forms.py openstack_dashboard/dashboards/identity/identity_providers/panel.py openstack_dashboard/dashboards/identity/identity_providers/tables.py openstack_dashboard/dashboards/identity/identity_providers/tabs.py openstack_dashboard/dashboards/identity/identity_providers/tests.py openstack_dashboard/dashboards/identity/identity_providers/urls.py openstack_dashboard/dashboards/identity/identity_providers/views.py openstack_dashboard/dashboards/identity/identity_providers/protocols/__init__.py openstack_dashboard/dashboards/identity/identity_providers/protocols/forms.py openstack_dashboard/dashboards/identity/identity_providers/protocols/tables.py openstack_dashboard/dashboards/identity/identity_providers/protocols/tests.py openstack_dashboard/dashboards/identity/identity_providers/protocols/urls.py openstack_dashboard/dashboards/identity/identity_providers/protocols/views.py openstack_dashboard/dashboards/identity/identity_providers/templates/identity_providers/_detail_overview.html openstack_dashboard/dashboards/identity/identity_providers/templates/identity_providers/_register.html openstack_dashboard/dashboards/identity/identity_providers/templates/identity_providers/_update.html openstack_dashboard/dashboards/identity/identity_providers/templates/identity_providers/index.html openstack_dashboard/dashboards/identity/identity_providers/templates/identity_providers/register.html openstack_dashboard/dashboards/identity/identity_providers/templates/identity_providers/update.html openstack_dashboard/dashboards/identity/identity_providers/templates/identity_providers/protocols/_create.html openstack_dashboard/dashboards/identity/identity_providers/templates/identity_providers/protocols/create.html openstack_dashboard/dashboards/identity/mappings/__init__.py openstack_dashboard/dashboards/identity/mappings/forms.py openstack_dashboard/dashboards/identity/mappings/panel.py openstack_dashboard/dashboards/identity/mappings/tables.py openstack_dashboard/dashboards/identity/mappings/tests.py openstack_dashboard/dashboards/identity/mappings/urls.py openstack_dashboard/dashboards/identity/mappings/views.py openstack_dashboard/dashboards/identity/mappings/templates/mappings/_create.html openstack_dashboard/dashboards/identity/mappings/templates/mappings/_update.html openstack_dashboard/dashboards/identity/mappings/templates/mappings/create.html openstack_dashboard/dashboards/identity/mappings/templates/mappings/index.html openstack_dashboard/dashboards/identity/mappings/templates/mappings/update.html openstack_dashboard/dashboards/identity/ngusers/__init__.py openstack_dashboard/dashboards/identity/ngusers/panel.py openstack_dashboard/dashboards/identity/ngusers/urls.py openstack_dashboard/dashboards/identity/ngusers/views.py openstack_dashboard/dashboards/identity/ngusers/templates/ngusers/index.html openstack_dashboard/dashboards/identity/projects/__init__.py openstack_dashboard/dashboards/identity/projects/panel.py openstack_dashboard/dashboards/identity/projects/tables.py openstack_dashboard/dashboards/identity/projects/tests.py openstack_dashboard/dashboards/identity/projects/urls.py openstack_dashboard/dashboards/identity/projects/views.py openstack_dashboard/dashboards/identity/projects/workflows.py openstack_dashboard/dashboards/identity/projects/templates/projects/_common_horizontal_form.html openstack_dashboard/dashboards/identity/projects/templates/projects/_detail_overview.html openstack_dashboard/dashboards/identity/projects/templates/projects/detail.html openstack_dashboard/dashboards/identity/projects/templates/projects/index.html openstack_dashboard/dashboards/identity/projects/templates/projects/usage.html openstack_dashboard/dashboards/identity/roles/__init__.py openstack_dashboard/dashboards/identity/roles/forms.py openstack_dashboard/dashboards/identity/roles/panel.py openstack_dashboard/dashboards/identity/roles/tables.py openstack_dashboard/dashboards/identity/roles/tests.py openstack_dashboard/dashboards/identity/roles/urls.py openstack_dashboard/dashboards/identity/roles/views.py openstack_dashboard/dashboards/identity/roles/templates/roles/_create.html openstack_dashboard/dashboards/identity/roles/templates/roles/_update.html openstack_dashboard/dashboards/identity/roles/templates/roles/create.html openstack_dashboard/dashboards/identity/roles/templates/roles/index.html openstack_dashboard/dashboards/identity/roles/templates/roles/update.html openstack_dashboard/dashboards/identity/static/dashboard/identity/identity.module.js openstack_dashboard/dashboards/identity/static/dashboard/identity/identity.module.spec.js openstack_dashboard/dashboards/identity/static/dashboard/identity/projects/projects.module.js openstack_dashboard/dashboards/identity/static/dashboard/identity/projects/projects.module.spec.js openstack_dashboard/dashboards/identity/static/dashboard/identity/users/users.module.js openstack_dashboard/dashboards/identity/static/dashboard/identity/users/users.module.spec.js openstack_dashboard/dashboards/identity/static/dashboard/identity/users/table/table.controller.js openstack_dashboard/dashboards/identity/static/dashboard/identity/users/table/table.controller.spec.js openstack_dashboard/dashboards/identity/static/dashboard/identity/users/table/table.html openstack_dashboard/dashboards/identity/users/__init__.py openstack_dashboard/dashboards/identity/users/forms.py openstack_dashboard/dashboards/identity/users/panel.py openstack_dashboard/dashboards/identity/users/tables.py openstack_dashboard/dashboards/identity/users/tests.py openstack_dashboard/dashboards/identity/users/urls.py openstack_dashboard/dashboards/identity/users/views.py openstack_dashboard/dashboards/identity/users/templates/users/_change_password.html openstack_dashboard/dashboards/identity/users/templates/users/_create.html openstack_dashboard/dashboards/identity/users/templates/users/_detail_overview.html openstack_dashboard/dashboards/identity/users/templates/users/_update.html openstack_dashboard/dashboards/identity/users/templates/users/change_password.html openstack_dashboard/dashboards/identity/users/templates/users/create.html openstack_dashboard/dashboards/identity/users/templates/users/detail.html openstack_dashboard/dashboards/identity/users/templates/users/index.html openstack_dashboard/dashboards/identity/users/templates/users/update.html openstack_dashboard/dashboards/project/__init__.py openstack_dashboard/dashboards/project/dashboard.py openstack_dashboard/dashboards/project/access_and_security/__init__.py openstack_dashboard/dashboards/project/access_and_security/panel.py openstack_dashboard/dashboards/project/access_and_security/tabs.py openstack_dashboard/dashboards/project/access_and_security/tests.py openstack_dashboard/dashboards/project/access_and_security/urls.py openstack_dashboard/dashboards/project/access_and_security/views.py openstack_dashboard/dashboards/project/access_and_security/api_access/__init__.py openstack_dashboard/dashboards/project/access_and_security/api_access/forms.py openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py openstack_dashboard/dashboards/project/access_and_security/api_access/tests.py openstack_dashboard/dashboards/project/access_and_security/api_access/urls.py openstack_dashboard/dashboards/project/access_and_security/api_access/views.py openstack_dashboard/dashboards/project/access_and_security/floating_ips/__init__.py openstack_dashboard/dashboards/project/access_and_security/floating_ips/forms.py openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py openstack_dashboard/dashboards/project/access_and_security/floating_ips/urls.py openstack_dashboard/dashboards/project/access_and_security/floating_ips/views.py openstack_dashboard/dashboards/project/access_and_security/floating_ips/workflows.py openstack_dashboard/dashboards/project/access_and_security/keypairs/__init__.py openstack_dashboard/dashboards/project/access_and_security/keypairs/forms.py openstack_dashboard/dashboards/project/access_and_security/keypairs/tables.py openstack_dashboard/dashboards/project/access_and_security/keypairs/tests.py openstack_dashboard/dashboards/project/access_and_security/keypairs/urls.py openstack_dashboard/dashboards/project/access_and_security/keypairs/views.py openstack_dashboard/dashboards/project/access_and_security/security_groups/__init__.py openstack_dashboard/dashboards/project/access_and_security/security_groups/forms.py openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py openstack_dashboard/dashboards/project/access_and_security/security_groups/urls.py openstack_dashboard/dashboards/project/access_and_security/security_groups/views.py openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/index.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/_credentials.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/_recreate_credentials.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/credentials.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/ec2rc.sh.template openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/openrc.sh.template openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/openrc_v2.sh.template openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/recreate_credentials.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/floating_ips/_allocate.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/floating_ips/allocate.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/_create.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/_import.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/create.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/detail.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/download.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/import.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/_add_rule.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/_create.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/_update.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/add_rule.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/create.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/detail.html openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/update.html openstack_dashboard/dashboards/project/containers/__init__.py openstack_dashboard/dashboards/project/containers/browsers.py openstack_dashboard/dashboards/project/containers/forms.py openstack_dashboard/dashboards/project/containers/panel.py openstack_dashboard/dashboards/project/containers/tables.py openstack_dashboard/dashboards/project/containers/tests.py openstack_dashboard/dashboards/project/containers/urls.py openstack_dashboard/dashboards/project/containers/utils.py openstack_dashboard/dashboards/project/containers/views.py openstack_dashboard/dashboards/project/containers/templates/containers/_container_detail.html openstack_dashboard/dashboards/project/containers/templates/containers/_container_loader.html openstack_dashboard/dashboards/project/containers/templates/containers/_container_metadata.html openstack_dashboard/dashboards/project/containers/templates/containers/_copy.html openstack_dashboard/dashboards/project/containers/templates/containers/_create.html openstack_dashboard/dashboards/project/containers/templates/containers/_create_pseudo_folder.html openstack_dashboard/dashboards/project/containers/templates/containers/_object_detail.html openstack_dashboard/dashboards/project/containers/templates/containers/_update.html openstack_dashboard/dashboards/project/containers/templates/containers/_upload.html openstack_dashboard/dashboards/project/containers/templates/containers/container_detail.html openstack_dashboard/dashboards/project/containers/templates/containers/copy.html openstack_dashboard/dashboards/project/containers/templates/containers/create.html openstack_dashboard/dashboards/project/containers/templates/containers/create_pseudo_folder.html openstack_dashboard/dashboards/project/containers/templates/containers/index.html openstack_dashboard/dashboards/project/containers/templates/containers/ngindex.html openstack_dashboard/dashboards/project/containers/templates/containers/object_detail.html openstack_dashboard/dashboards/project/containers/templates/containers/update.html openstack_dashboard/dashboards/project/containers/templates/containers/upload.html openstack_dashboard/dashboards/project/firewalls/__init__.py openstack_dashboard/dashboards/project/firewalls/forms.py openstack_dashboard/dashboards/project/firewalls/panel.py openstack_dashboard/dashboards/project/firewalls/tables.py openstack_dashboard/dashboards/project/firewalls/tabs.py openstack_dashboard/dashboards/project/firewalls/tests.py openstack_dashboard/dashboards/project/firewalls/urls.py openstack_dashboard/dashboards/project/firewalls/views.py openstack_dashboard/dashboards/project/firewalls/workflows.py openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_add_router_to_firewall.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_firewall_details.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_insert_rule_to_policy.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_policy_details.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_remove_router_from_firewall.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_remove_rule_from_policy.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_rule_details.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_update_router_help.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_update_routers.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_update_rule_help.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_update_rules.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_updatefirewall.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_updatepolicy.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/_updaterule.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/add_router_to_firewall.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/addfirewall.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/addpolicy.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/addrule.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/details_tabs.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/insert_rule_to_policy.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/remove_router_from_firewall.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/remove_rule_from_policy.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/updatefirewall.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/updatepolicy.html openstack_dashboard/dashboards/project/firewalls/templates/firewalls/updaterule.html openstack_dashboard/dashboards/project/images/__init__.py openstack_dashboard/dashboards/project/images/panel.py openstack_dashboard/dashboards/project/images/tests.py openstack_dashboard/dashboards/project/images/urls.py openstack_dashboard/dashboards/project/images/utils.py openstack_dashboard/dashboards/project/images/views.py openstack_dashboard/dashboards/project/images/images/__init__.py openstack_dashboard/dashboards/project/images/images/forms.py openstack_dashboard/dashboards/project/images/images/tables.py openstack_dashboard/dashboards/project/images/images/tabs.py openstack_dashboard/dashboards/project/images/images/tests.py openstack_dashboard/dashboards/project/images/images/urls.py openstack_dashboard/dashboards/project/images/images/views.py openstack_dashboard/dashboards/project/images/snapshots/__init__.py openstack_dashboard/dashboards/project/images/snapshots/forms.py openstack_dashboard/dashboards/project/images/snapshots/tests.py openstack_dashboard/dashboards/project/images/snapshots/urls.py openstack_dashboard/dashboards/project/images/snapshots/views.py openstack_dashboard/dashboards/project/images/templates/images/index.html openstack_dashboard/dashboards/project/images/templates/images/images/_create.html openstack_dashboard/dashboards/project/images/templates/images/images/_detail_overview.html openstack_dashboard/dashboards/project/images/templates/images/images/_update.html openstack_dashboard/dashboards/project/images/templates/images/images/create.html openstack_dashboard/dashboards/project/images/templates/images/images/update.html openstack_dashboard/dashboards/project/images/templates/images/snapshots/_create.html openstack_dashboard/dashboards/project/images/templates/images/snapshots/create.html openstack_dashboard/dashboards/project/instances/__init__.py openstack_dashboard/dashboards/project/instances/audit_tables.py openstack_dashboard/dashboards/project/instances/console.py openstack_dashboard/dashboards/project/instances/forms.py openstack_dashboard/dashboards/project/instances/panel.py openstack_dashboard/dashboards/project/instances/tables.py openstack_dashboard/dashboards/project/instances/tabs.py openstack_dashboard/dashboards/project/instances/tests.py openstack_dashboard/dashboards/project/instances/urls.py openstack_dashboard/dashboards/project/instances/utils.py openstack_dashboard/dashboards/project/instances/views.py openstack_dashboard/dashboards/project/instances/templates/instances/_attach_interface.html openstack_dashboard/dashboards/project/instances/templates/instances/_decryptpassword.html openstack_dashboard/dashboards/project/instances/templates/instances/_detach_interface.html openstack_dashboard/dashboards/project/instances/templates/instances/_detail_audit.html openstack_dashboard/dashboards/project/instances/templates/instances/_detail_console.html openstack_dashboard/dashboards/project/instances/templates/instances/_detail_log.html openstack_dashboard/dashboards/project/instances/templates/instances/_detail_overview.html openstack_dashboard/dashboards/project/instances/templates/instances/_flavors_and_quotas.html openstack_dashboard/dashboards/project/instances/templates/instances/_instance_flavor.html openstack_dashboard/dashboards/project/instances/templates/instances/_instance_ips.html openstack_dashboard/dashboards/project/instances/templates/instances/_launch_advanced_help.html openstack_dashboard/dashboards/project/instances/templates/instances/_launch_customize_help.html openstack_dashboard/dashboards/project/instances/templates/instances/_launch_details_help.html openstack_dashboard/dashboards/project/instances/templates/instances/_launch_network_help.html openstack_dashboard/dashboards/project/instances/templates/instances/_launch_network_ports_help.html openstack_dashboard/dashboards/project/instances/templates/instances/_launch_volumes_help.html openstack_dashboard/dashboards/project/instances/templates/instances/_rebuild.html openstack_dashboard/dashboards/project/instances/templates/instances/_update_networks.html openstack_dashboard/dashboards/project/instances/templates/instances/attach_interface.html openstack_dashboard/dashboards/project/instances/templates/instances/decryptpassword.html openstack_dashboard/dashboards/project/instances/templates/instances/detach_interface.html openstack_dashboard/dashboards/project/instances/templates/instances/index.html openstack_dashboard/dashboards/project/instances/templates/instances/rebuild.html openstack_dashboard/dashboards/project/instances/templates/instances/serial_console.html openstack_dashboard/dashboards/project/instances/workflows/__init__.py openstack_dashboard/dashboards/project/instances/workflows/create_instance.py openstack_dashboard/dashboards/project/instances/workflows/resize_instance.py openstack_dashboard/dashboards/project/instances/workflows/update_instance.py openstack_dashboard/dashboards/project/loadbalancers/__init__.py openstack_dashboard/dashboards/project/loadbalancers/forms.py openstack_dashboard/dashboards/project/loadbalancers/panel.py openstack_dashboard/dashboards/project/loadbalancers/tables.py openstack_dashboard/dashboards/project/loadbalancers/tabs.py openstack_dashboard/dashboards/project/loadbalancers/tests.py openstack_dashboard/dashboards/project/loadbalancers/urls.py openstack_dashboard/dashboards/project/loadbalancers/utils.py openstack_dashboard/dashboards/project/loadbalancers/views.py openstack_dashboard/dashboards/project/loadbalancers/workflows.py openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_create_pool_help.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_create_vip_help.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_member_details.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_members_tab.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_monitor_details.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_monitors_tab.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_pool_details.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_pool_table_subnet_cell.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_pool_table_vip_cell.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_pools_tab.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_updatemember.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_updatemonitor.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_updatepool.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_updatevip.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_vip_details.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/details_tabs.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/updatemember.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/updatemonitor.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/updatepool.html openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/updatevip.html openstack_dashboard/dashboards/project/network_topology/__init__.py openstack_dashboard/dashboards/project/network_topology/panel.py openstack_dashboard/dashboards/project/network_topology/tests.py openstack_dashboard/dashboards/project/network_topology/urls.py openstack_dashboard/dashboards/project/network_topology/views.py openstack_dashboard/dashboards/project/network_topology/instances/__init__.py openstack_dashboard/dashboards/project/network_topology/instances/tables.py openstack_dashboard/dashboards/project/network_topology/networks/__init__.py openstack_dashboard/dashboards/project/network_topology/networks/tables.py openstack_dashboard/dashboards/project/network_topology/ports/__init__.py openstack_dashboard/dashboards/project/network_topology/ports/tables.py openstack_dashboard/dashboards/project/network_topology/routers/__init__.py openstack_dashboard/dashboards/project/network_topology/routers/tables.py openstack_dashboard/dashboards/project/network_topology/subnets/__init__.py openstack_dashboard/dashboards/project/network_topology/subnets/tables.py openstack_dashboard/dashboards/project/network_topology/templates/network_topology/_create_router.html openstack_dashboard/dashboards/project/network_topology/templates/network_topology/_post_massage.html openstack_dashboard/dashboards/project/network_topology/templates/network_topology/create_router.html openstack_dashboard/dashboards/project/network_topology/templates/network_topology/iframe.html openstack_dashboard/dashboards/project/network_topology/templates/network_topology/index.html openstack_dashboard/dashboards/project/network_topology/templates/network_topology/client_side/_balloon_container.html openstack_dashboard/dashboards/project/network_topology/templates/network_topology/client_side/_balloon_device.html openstack_dashboard/dashboards/project/network_topology/templates/network_topology/client_side/_balloon_instance.html openstack_dashboard/dashboards/project/network_topology/templates/network_topology/client_side/_balloon_net.html openstack_dashboard/dashboards/project/network_topology/templates/network_topology/client_side/_balloon_port.html openstack_dashboard/dashboards/project/networks/__init__.py openstack_dashboard/dashboards/project/networks/forms.py openstack_dashboard/dashboards/project/networks/panel.py openstack_dashboard/dashboards/project/networks/tables.py openstack_dashboard/dashboards/project/networks/tests.py openstack_dashboard/dashboards/project/networks/urls.py openstack_dashboard/dashboards/project/networks/views.py openstack_dashboard/dashboards/project/networks/workflows.py openstack_dashboard/dashboards/project/networks/ports/__init__.py openstack_dashboard/dashboards/project/networks/ports/forms.py openstack_dashboard/dashboards/project/networks/ports/tables.py openstack_dashboard/dashboards/project/networks/ports/tabs.py openstack_dashboard/dashboards/project/networks/ports/tests.py openstack_dashboard/dashboards/project/networks/ports/urls.py openstack_dashboard/dashboards/project/networks/ports/views.py openstack_dashboard/dashboards/project/networks/subnets/__init__.py openstack_dashboard/dashboards/project/networks/subnets/tables.py openstack_dashboard/dashboards/project/networks/subnets/tabs.py openstack_dashboard/dashboards/project/networks/subnets/urls.py openstack_dashboard/dashboards/project/networks/subnets/utils.py openstack_dashboard/dashboards/project/networks/subnets/views.py openstack_dashboard/dashboards/project/networks/subnets/workflows.py openstack_dashboard/dashboards/project/networks/templates/networks/_create.html openstack_dashboard/dashboards/project/networks/templates/networks/_detail_overview.html openstack_dashboard/dashboards/project/networks/templates/networks/_network_ips.html openstack_dashboard/dashboards/project/networks/templates/networks/_update.html openstack_dashboard/dashboards/project/networks/templates/networks/create.html openstack_dashboard/dashboards/project/networks/templates/networks/detail.html openstack_dashboard/dashboards/project/networks/templates/networks/index.html openstack_dashboard/dashboards/project/networks/templates/networks/update.html openstack_dashboard/dashboards/project/networks/templates/networks/ports/_detail_overview.html openstack_dashboard/dashboards/project/networks/templates/networks/ports/_port_ips.html openstack_dashboard/dashboards/project/networks/templates/networks/ports/_update.html openstack_dashboard/dashboards/project/networks/templates/networks/ports/update.html openstack_dashboard/dashboards/project/networks/templates/networks/subnets/_detail_overview.html openstack_dashboard/dashboards/project/ngimages/__init__.py openstack_dashboard/dashboards/project/ngimages/panel.py openstack_dashboard/dashboards/project/ngimages/urls.py openstack_dashboard/dashboards/project/ngimages/views.py openstack_dashboard/dashboards/project/ngimages/templates/ngimages/index.html openstack_dashboard/dashboards/project/overview/__init__.py openstack_dashboard/dashboards/project/overview/panel.py openstack_dashboard/dashboards/project/overview/tests.py openstack_dashboard/dashboards/project/overview/urls.py openstack_dashboard/dashboards/project/overview/views.py openstack_dashboard/dashboards/project/overview/templates/overview/usage.csv openstack_dashboard/dashboards/project/overview/templates/overview/usage.html openstack_dashboard/dashboards/project/routers/__init__.py openstack_dashboard/dashboards/project/routers/forms.py openstack_dashboard/dashboards/project/routers/panel.py openstack_dashboard/dashboards/project/routers/tables.py openstack_dashboard/dashboards/project/routers/tabs.py openstack_dashboard/dashboards/project/routers/tests.py openstack_dashboard/dashboards/project/routers/urls.py openstack_dashboard/dashboards/project/routers/views.py openstack_dashboard/dashboards/project/routers/extensions/__init__.py openstack_dashboard/dashboards/project/routers/extensions/extraroutes/__init__.py openstack_dashboard/dashboards/project/routers/extensions/extraroutes/forms.py openstack_dashboard/dashboards/project/routers/extensions/extraroutes/tables.py openstack_dashboard/dashboards/project/routers/extensions/extraroutes/tabs.py openstack_dashboard/dashboards/project/routers/extensions/extraroutes/views.py openstack_dashboard/dashboards/project/routers/extensions/routerrules/__init__.py openstack_dashboard/dashboards/project/routers/extensions/routerrules/forms.py openstack_dashboard/dashboards/project/routers/extensions/routerrules/rulemanager.py openstack_dashboard/dashboards/project/routers/extensions/routerrules/tables.py openstack_dashboard/dashboards/project/routers/extensions/routerrules/tabs.py openstack_dashboard/dashboards/project/routers/extensions/routerrules/views.py openstack_dashboard/dashboards/project/routers/ports/__init__.py openstack_dashboard/dashboards/project/routers/ports/forms.py openstack_dashboard/dashboards/project/routers/ports/tables.py openstack_dashboard/dashboards/project/routers/ports/tabs.py openstack_dashboard/dashboards/project/routers/ports/urls.py openstack_dashboard/dashboards/project/routers/ports/views.py openstack_dashboard/dashboards/project/routers/templates/routers/_create.html openstack_dashboard/dashboards/project/routers/templates/routers/_detail_overview.html openstack_dashboard/dashboards/project/routers/templates/routers/_update.html openstack_dashboard/dashboards/project/routers/templates/routers/create.html openstack_dashboard/dashboards/project/routers/templates/routers/index.html openstack_dashboard/dashboards/project/routers/templates/routers/update.html openstack_dashboard/dashboards/project/routers/templates/routers/extensions/routerroutes/_create.html openstack_dashboard/dashboards/project/routers/templates/routers/extensions/routerroutes/create.html openstack_dashboard/dashboards/project/routers/templates/routers/extensions/routerrules/_create.html openstack_dashboard/dashboards/project/routers/templates/routers/extensions/routerrules/create.html openstack_dashboard/dashboards/project/routers/templates/routers/extensions/routerrules/grid.html openstack_dashboard/dashboards/project/routers/templates/routers/ports/_create.html openstack_dashboard/dashboards/project/routers/templates/routers/ports/_setgateway.html openstack_dashboard/dashboards/project/routers/templates/routers/ports/create.html openstack_dashboard/dashboards/project/routers/templates/routers/ports/setgateway.html openstack_dashboard/dashboards/project/stacks/__init__.py openstack_dashboard/dashboards/project/stacks/api.py openstack_dashboard/dashboards/project/stacks/forms.py openstack_dashboard/dashboards/project/stacks/mappings.py openstack_dashboard/dashboards/project/stacks/panel.py openstack_dashboard/dashboards/project/stacks/sro.py openstack_dashboard/dashboards/project/stacks/tables.py openstack_dashboard/dashboards/project/stacks/tabs.py openstack_dashboard/dashboards/project/stacks/tests.py openstack_dashboard/dashboards/project/stacks/urls.py openstack_dashboard/dashboards/project/stacks/views.py openstack_dashboard/dashboards/project/stacks/resource_types/__init__.py openstack_dashboard/dashboards/project/stacks/resource_types/panel.py openstack_dashboard/dashboards/project/stacks/resource_types/tables.py openstack_dashboard/dashboards/project/stacks/resource_types/tabs.py openstack_dashboard/dashboards/project/stacks/resource_types/tests.py openstack_dashboard/dashboards/project/stacks/resource_types/urls.py openstack_dashboard/dashboards/project/stacks/resource_types/views.py openstack_dashboard/dashboards/project/stacks/resource_types/templates/stacks.resource_types/_details.html openstack_dashboard/dashboards/project/stacks/resource_types/templates/stacks.resource_types/index.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_change_template.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_create.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_events.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_overview.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_resources.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_topology.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_template.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_resource_info.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_resource_overview.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_select_template.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_stack_info.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_stack_template.html openstack_dashboard/dashboards/project/stacks/templates/stacks/_update.html openstack_dashboard/dashboards/project/stacks/templates/stacks/change_template.html openstack_dashboard/dashboards/project/stacks/templates/stacks/create.html openstack_dashboard/dashboards/project/stacks/templates/stacks/index.html openstack_dashboard/dashboards/project/stacks/templates/stacks/preview.html openstack_dashboard/dashboards/project/stacks/templates/stacks/preview_details.html openstack_dashboard/dashboards/project/stacks/templates/stacks/preview_template.html openstack_dashboard/dashboards/project/stacks/templates/stacks/select_template.html openstack_dashboard/dashboards/project/stacks/templates/stacks/update.html openstack_dashboard/dashboards/project/static/dashboard/project/project.module.js openstack_dashboard/dashboards/project/static/dashboard/project/project.module.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/_containers.scss openstack_dashboard/dashboards/project/static/dashboard/project/containers/containers-model.service.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/containers-model.service.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/containers.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/containers.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/containers.html openstack_dashboard/dashboards/project/static/dashboard/project/containers/containers.module.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/containers.module.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/create-container-modal.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/create-container-modal.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/create-container-modal.html openstack_dashboard/dashboards/project/static/dashboard/project/containers/create-folder-modal.html openstack_dashboard/dashboards/project/static/dashboard/project/containers/file-change-directive.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/file-change-directive.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/object-details-modal.html openstack_dashboard/dashboards/project/static/dashboard/project/containers/objects-row-actions.service.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/objects-row-actions.service.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/objects.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/objects.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/objects.html openstack_dashboard/dashboards/project/static/dashboard/project/containers/select-container.html openstack_dashboard/dashboards/project/static/dashboard/project/containers/upload-object-controller.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/upload-object-controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/containers/upload-object-modal.html openstack_dashboard/dashboards/project/static/dashboard/project/images/images.module.js openstack_dashboard/dashboards/project/static/dashboard/project/images/images.module.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/workflow.module.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/workflow.module.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-modal.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-modal.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-modal.service.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-modal.service.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-wizard.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-wizard.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-workflow.service.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-workflow.service.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance.module.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance.module.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/configuration/configuration.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/configuration/configuration.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/configuration/configuration.help.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/configuration/configuration.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/details/details.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/details/details.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/details/details.help.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/details/details.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/flavor/flavor.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/flavor/flavor.help.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/flavor/flavor.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/flavor/flavor.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/flavor/select-flavor-table.directive.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/flavor/select-flavor-table.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/create-keypair.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/create-keypair.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/create-keypair.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/import-keypair.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/import-keypair.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/import-keypair.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair-details.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.help.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/metadata/metadata.help.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/metadata/metadata.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/metadata/metadata.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/network/network.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/network/network.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/network/network.help.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/network/network.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.help.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/security-groups/security-group-details.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/security-groups/security-groups.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/security-groups/security-groups.help.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/security-groups/security-groups.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/security-groups/security-groups.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source-details.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.spec.js openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.help.html openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.html openstack_dashboard/dashboards/project/volumes/__init__.py openstack_dashboard/dashboards/project/volumes/panel.py openstack_dashboard/dashboards/project/volumes/tabs.py openstack_dashboard/dashboards/project/volumes/test.py openstack_dashboard/dashboards/project/volumes/urls.py openstack_dashboard/dashboards/project/volumes/utils.py openstack_dashboard/dashboards/project/volumes/views.py openstack_dashboard/dashboards/project/volumes/backups/__init__.py openstack_dashboard/dashboards/project/volumes/backups/forms.py openstack_dashboard/dashboards/project/volumes/backups/tables.py openstack_dashboard/dashboards/project/volumes/backups/tabs.py openstack_dashboard/dashboards/project/volumes/backups/tests.py openstack_dashboard/dashboards/project/volumes/backups/urls.py openstack_dashboard/dashboards/project/volumes/backups/views.py openstack_dashboard/dashboards/project/volumes/cgroups/__init__.py openstack_dashboard/dashboards/project/volumes/cgroups/forms.py openstack_dashboard/dashboards/project/volumes/cgroups/tables.py openstack_dashboard/dashboards/project/volumes/cgroups/tabs.py openstack_dashboard/dashboards/project/volumes/cgroups/tests.py openstack_dashboard/dashboards/project/volumes/cgroups/urls.py openstack_dashboard/dashboards/project/volumes/cgroups/views.py openstack_dashboard/dashboards/project/volumes/cgroups/workflows.py openstack_dashboard/dashboards/project/volumes/snapshots/__init__.py openstack_dashboard/dashboards/project/volumes/snapshots/forms.py openstack_dashboard/dashboards/project/volumes/snapshots/tables.py openstack_dashboard/dashboards/project/volumes/snapshots/tabs.py openstack_dashboard/dashboards/project/volumes/snapshots/tests.py openstack_dashboard/dashboards/project/volumes/snapshots/urls.py openstack_dashboard/dashboards/project/volumes/snapshots/views.py openstack_dashboard/dashboards/project/volumes/templates/volumes/index.html openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/_create_backup.html openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/_detail_overview.html openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/_restore_backup.html openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/create_backup.html openstack_dashboard/dashboards/project/volumes/templates/volumes/backups/restore_backup.html openstack_dashboard/dashboards/project/volumes/templates/volumes/cgroups/_delete.html openstack_dashboard/dashboards/project/volumes/templates/volumes/cgroups/_detail_overview.html openstack_dashboard/dashboards/project/volumes/templates/volumes/cgroups/_update.html openstack_dashboard/dashboards/project/volumes/templates/volumes/cgroups/create.html openstack_dashboard/dashboards/project/volumes/templates/volumes/cgroups/delete.html openstack_dashboard/dashboards/project/volumes/templates/volumes/cgroups/update.html openstack_dashboard/dashboards/project/volumes/templates/volumes/snapshots/_detail_overview.html openstack_dashboard/dashboards/project/volumes/templates/volumes/snapshots/_update.html openstack_dashboard/dashboards/project/volumes/templates/volumes/snapshots/update.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_accept_transfer.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_attach.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_create.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_create_snapshot.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_create_transfer.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_detail_overview.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_encryption_detail_overview.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_extend.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_extend_limits.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_limits.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_retype.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_show_transfer.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_snapshot_limits.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_update.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/_upload_to_image.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/accept_transfer.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/attach.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/create.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/create_snapshot.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/create_transfer.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/encryption_detail.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/extend.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/retype.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/show_transfer.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/update.html openstack_dashboard/dashboards/project/volumes/templates/volumes/volumes/upload_to_image.html openstack_dashboard/dashboards/project/volumes/volumes/__init__.py openstack_dashboard/dashboards/project/volumes/volumes/forms.py openstack_dashboard/dashboards/project/volumes/volumes/tables.py openstack_dashboard/dashboards/project/volumes/volumes/tabs.py openstack_dashboard/dashboards/project/volumes/volumes/tests.py openstack_dashboard/dashboards/project/volumes/volumes/urls.py openstack_dashboard/dashboards/project/volumes/volumes/views.py openstack_dashboard/dashboards/project/vpn/__init__.py openstack_dashboard/dashboards/project/vpn/forms.py openstack_dashboard/dashboards/project/vpn/panel.py openstack_dashboard/dashboards/project/vpn/tables.py openstack_dashboard/dashboards/project/vpn/tabs.py openstack_dashboard/dashboards/project/vpn/tests.py openstack_dashboard/dashboards/project/vpn/urls.py openstack_dashboard/dashboards/project/vpn/views.py openstack_dashboard/dashboards/project/vpn/workflows.py openstack_dashboard/dashboards/project/vpn/templates/vpn/_ikepolicy_details.html openstack_dashboard/dashboards/project/vpn/templates/vpn/_ipsecpolicy_details.html openstack_dashboard/dashboards/project/vpn/templates/vpn/_ipsecsiteconnection_details.html openstack_dashboard/dashboards/project/vpn/templates/vpn/_update_ikepolicy.html openstack_dashboard/dashboards/project/vpn/templates/vpn/_update_ipsecpolicy.html openstack_dashboard/dashboards/project/vpn/templates/vpn/_update_ipsecsiteconnection.html openstack_dashboard/dashboards/project/vpn/templates/vpn/_update_vpnservice.html openstack_dashboard/dashboards/project/vpn/templates/vpn/_vpn_ips.html openstack_dashboard/dashboards/project/vpn/templates/vpn/_vpnservice_details.html openstack_dashboard/dashboards/project/vpn/templates/vpn/index.html openstack_dashboard/dashboards/project/vpn/templates/vpn/update_ikepolicy.html openstack_dashboard/dashboards/project/vpn/templates/vpn/update_ipsecpolicy.html openstack_dashboard/dashboards/project/vpn/templates/vpn/update_ipsecsiteconnection.html openstack_dashboard/dashboards/project/vpn/templates/vpn/update_vpnservice.html openstack_dashboard/dashboards/settings/__init__.py openstack_dashboard/dashboards/settings/dashboard.py openstack_dashboard/dashboards/settings/password/__init__.py openstack_dashboard/dashboards/settings/password/forms.py openstack_dashboard/dashboards/settings/password/panel.py openstack_dashboard/dashboards/settings/password/tests.py openstack_dashboard/dashboards/settings/password/urls.py openstack_dashboard/dashboards/settings/password/views.py openstack_dashboard/dashboards/settings/password/templates/password/_change.html openstack_dashboard/dashboards/settings/password/templates/password/change.html openstack_dashboard/dashboards/settings/user/__init__.py openstack_dashboard/dashboards/settings/user/forms.py openstack_dashboard/dashboards/settings/user/panel.py openstack_dashboard/dashboards/settings/user/tests.py openstack_dashboard/dashboards/settings/user/urls.py openstack_dashboard/dashboards/settings/user/views.py openstack_dashboard/dashboards/settings/user/templates/user/_settings.html openstack_dashboard/dashboards/settings/user/templates/user/settings.html openstack_dashboard/django_pyscss_fix/__init__.py openstack_dashboard/enabled/_1000_project.py openstack_dashboard/enabled/_1010_compute_panel_group.py openstack_dashboard/enabled/_1020_project_overview_panel.py openstack_dashboard/enabled/_1030_project_instances_panel.py openstack_dashboard/enabled/_1040_project_volumes_panel.py openstack_dashboard/enabled/_1050_project_images_panel.py openstack_dashboard/enabled/_1051_project_ng_images_panel.py openstack_dashboard/enabled/_1060_project_access_panel.py openstack_dashboard/enabled/_1410_network_panel_group.py openstack_dashboard/enabled/_1420_project_network_topology_panel.py openstack_dashboard/enabled/_1430_project_network_panel.py openstack_dashboard/enabled/_1440_project_routers_panel.py openstack_dashboard/enabled/_1450_project_loadbalancers_panel.py openstack_dashboard/enabled/_1460_project_firewalls_panel.py openstack_dashboard/enabled/_1470_project_vpn_panel.py openstack_dashboard/enabled/_1610_orchestration_panel_group.py openstack_dashboard/enabled/_1620_project_stacks_panel.py openstack_dashboard/enabled/_1630_project_resource_types_panel.py openstack_dashboard/enabled/_1910_object_store_panel_group.py openstack_dashboard/enabled/_1920_project_containers_panel.py openstack_dashboard/enabled/_2000_admin.py openstack_dashboard/enabled/_2010_admin_system_panel_group.py openstack_dashboard/enabled/_2020_admin_overview_panel.py openstack_dashboard/enabled/_2030_admin_metering_panel.py openstack_dashboard/enabled/_2040_admin_hypervisors_panel.py openstack_dashboard/enabled/_2050_admin_aggregates_panel.py openstack_dashboard/enabled/_2060_admin_instances_panel.py openstack_dashboard/enabled/_2070_admin_volumes_panel.py openstack_dashboard/enabled/_2080_admin_flavors_panel.py openstack_dashboard/enabled/_2081_admin_flavors_panel.py openstack_dashboard/enabled/_2090_admin_images_panel.py openstack_dashboard/enabled/_2100_admin_networks_panel.py openstack_dashboard/enabled/_2110_admin_routers_panel.py openstack_dashboard/enabled/_2120_admin_defaults_panel.py openstack_dashboard/enabled/_2130_admin_metadata_defs_panel.py openstack_dashboard/enabled/_2140_admin_info_panel.py openstack_dashboard/enabled/_3000_identity.py openstack_dashboard/enabled/_3010_identity_domains_panel.py openstack_dashboard/enabled/_3020_identity_projects_panel.py openstack_dashboard/enabled/_3030_identity_users_panel.py openstack_dashboard/enabled/_3031_identity_users_panel.py openstack_dashboard/enabled/_3040_identity_groups_panel.py openstack_dashboard/enabled/_3050_identity_roles_panel.py openstack_dashboard/enabled/_3060_federation_panel_group.py openstack_dashboard/enabled/_3070_identity_identity_providers_panel.py openstack_dashboard/enabled/_3080_identity_mappings_panel.py openstack_dashboard/enabled/_5000_settings.py openstack_dashboard/enabled/_50_admin_add_panel.py.example openstack_dashboard/enabled/_60_admin_remove_panel.py.example openstack_dashboard/enabled/_70_admin_default_panel.py.example openstack_dashboard/enabled/_80_admin_add_panel_group.py.example openstack_dashboard/enabled/_9001_developer.py openstack_dashboard/enabled/_9010_preview.py openstack_dashboard/enabled/_90_admin_add_panel_to_group.py.example openstack_dashboard/enabled/__init__.py openstack_dashboard/local/__init__.py openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/enabled/_50_settings.py.example openstack_dashboard/local/enabled/__init__.py openstack_dashboard/local/local_settings.d/_10_set_custom_theme.py.example openstack_dashboard/local/local_settings.d/_20_integration_tests_scaffolds.py.example openstack_dashboard/locale/django.pot openstack_dashboard/locale/djangojs.pot openstack_dashboard/locale/as/LC_MESSAGES/django.po openstack_dashboard/locale/bn_IN/LC_MESSAGES/django.po openstack_dashboard/locale/brx/LC_MESSAGES/django.po openstack_dashboard/locale/cs/LC_MESSAGES/django.po openstack_dashboard/locale/cs/LC_MESSAGES/djangojs.po openstack_dashboard/locale/de/LC_MESSAGES/django.po openstack_dashboard/locale/de/LC_MESSAGES/djangojs.po openstack_dashboard/locale/en_AU/LC_MESSAGES/django.po openstack_dashboard/locale/en_AU/LC_MESSAGES/djangojs.po openstack_dashboard/locale/en_GB/LC_MESSAGES/django.po openstack_dashboard/locale/en_GB/LC_MESSAGES/djangojs.po openstack_dashboard/locale/es/LC_MESSAGES/django.po openstack_dashboard/locale/es/LC_MESSAGES/djangojs.po openstack_dashboard/locale/fr/LC_MESSAGES/django.po openstack_dashboard/locale/fr/LC_MESSAGES/djangojs.po openstack_dashboard/locale/gu/LC_MESSAGES/django.po openstack_dashboard/locale/hi/LC_MESSAGES/django.po openstack_dashboard/locale/it/LC_MESSAGES/django.po openstack_dashboard/locale/it/LC_MESSAGES/djangojs.po openstack_dashboard/locale/ja/LC_MESSAGES/django.po openstack_dashboard/locale/ja/LC_MESSAGES/djangojs.po openstack_dashboard/locale/kn/LC_MESSAGES/django.po openstack_dashboard/locale/ko_KR/LC_MESSAGES/django.po openstack_dashboard/locale/ko_KR/LC_MESSAGES/djangojs.po openstack_dashboard/locale/kok/LC_MESSAGES/django.po openstack_dashboard/locale/ks/LC_MESSAGES/django.po openstack_dashboard/locale/mai/LC_MESSAGES/django.po openstack_dashboard/locale/mni/LC_MESSAGES/django.po openstack_dashboard/locale/mr/LC_MESSAGES/django.po openstack_dashboard/locale/ne/LC_MESSAGES/django.po openstack_dashboard/locale/nl_NL/LC_MESSAGES/django.po openstack_dashboard/locale/pa_IN/LC_MESSAGES/django.po openstack_dashboard/locale/pl_PL/LC_MESSAGES/django.po openstack_dashboard/locale/pl_PL/LC_MESSAGES/djangojs.po openstack_dashboard/locale/pt_BR/LC_MESSAGES/django.po openstack_dashboard/locale/pt_BR/LC_MESSAGES/djangojs.po openstack_dashboard/locale/ru/LC_MESSAGES/django.po openstack_dashboard/locale/ru/LC_MESSAGES/djangojs.po openstack_dashboard/locale/sr/LC_MESSAGES/django.po openstack_dashboard/locale/ta/LC_MESSAGES/django.po openstack_dashboard/locale/tr_TR/LC_MESSAGES/django.po openstack_dashboard/locale/tr_TR/LC_MESSAGES/djangojs.po openstack_dashboard/locale/ur/LC_MESSAGES/django.po openstack_dashboard/locale/zh_CN/LC_MESSAGES/django.po openstack_dashboard/locale/zh_CN/LC_MESSAGES/djangojs.po openstack_dashboard/locale/zh_TW/LC_MESSAGES/django.po openstack_dashboard/locale/zh_TW/LC_MESSAGES/djangojs.po openstack_dashboard/management/__init__.py openstack_dashboard/management/commands/__init__.py openstack_dashboard/management/commands/apache_vhost.conf.template openstack_dashboard/management/commands/horizon.wsgi.template openstack_dashboard/management/commands/make_web_conf.py openstack_dashboard/management/commands/migrate_settings.py openstack_dashboard/static/app/_app.scss openstack_dashboard/static/app/app.module.js openstack_dashboard/static/app/app.module.spec.js openstack_dashboard/static/app/redirect.controller.js openstack_dashboard/static/app/redirect.controller.spec.js openstack_dashboard/static/app/core/_core.scss openstack_dashboard/static/app/core/core.module.js openstack_dashboard/static/app/core/core.module.spec.js openstack_dashboard/static/app/core/cloud-services/cloud-services.module.js openstack_dashboard/static/app/core/cloud-services/hz-if-cinder-extensions.directive.js openstack_dashboard/static/app/core/cloud-services/hz-if-cinder-extensions.directive.spec.js openstack_dashboard/static/app/core/cloud-services/hz-if-neutron-extensions.directive.js openstack_dashboard/static/app/core/cloud-services/hz-if-neutron-extensions.directive.spec.js openstack_dashboard/static/app/core/cloud-services/hz-if-nova-extensions.directive.js openstack_dashboard/static/app/core/cloud-services/hz-if-nova-extensions.directive.spec.js openstack_dashboard/static/app/core/cloud-services/hz-if-policies.directive.js openstack_dashboard/static/app/core/cloud-services/hz-if-policies.directive.spec.js openstack_dashboard/static/app/core/cloud-services/hz-if-services.directive.js openstack_dashboard/static/app/core/cloud-services/hz-if-services.directive.spec.js openstack_dashboard/static/app/core/cloud-services/hz-if-settings.directive.js openstack_dashboard/static/app/core/cloud-services/hz-if-settings.directive.spec.js openstack_dashboard/static/app/core/cloud-services/hz-if-version.directive.js openstack_dashboard/static/app/core/cloud-services/hz-if-version.directive.spec.js openstack_dashboard/static/app/core/images/_images.scss openstack_dashboard/static/app/core/images/images.module.js openstack_dashboard/static/app/core/images/images.module.spec.js openstack_dashboard/static/app/core/images/actions/actions.module.js openstack_dashboard/static/app/core/images/actions/create-volume.service.js openstack_dashboard/static/app/core/images/actions/create-volume.service.spec.js openstack_dashboard/static/app/core/images/actions/delete-image.service.js openstack_dashboard/static/app/core/images/actions/delete-image.service.spec.js openstack_dashboard/static/app/core/images/actions/launch-instance.service.js openstack_dashboard/static/app/core/images/actions/launch-instance.service.spec.js openstack_dashboard/static/app/core/images/actions/update-metadata.action.service.js openstack_dashboard/static/app/core/images/actions/update-metadata.action.service.spec.js openstack_dashboard/static/app/core/images/detail/image-detail.controller.js openstack_dashboard/static/app/core/images/detail/image-detail.controller.spec.js openstack_dashboard/static/app/core/images/detail/image-detail.html openstack_dashboard/static/app/core/images/filters/image-status.filter.js openstack_dashboard/static/app/core/images/filters/image-status.filter.spec.js openstack_dashboard/static/app/core/images/filters/image-type.filter.js openstack_dashboard/static/app/core/images/filters/image-type.filter.spec.js openstack_dashboard/static/app/core/images/filters/image-visibility.filter.js openstack_dashboard/static/app/core/images/filters/image-visibility.filter.spec.js openstack_dashboard/static/app/core/images/steps/create-volume/create-volume.controller.js openstack_dashboard/static/app/core/images/steps/create-volume/create-volume.controller.spec.js openstack_dashboard/static/app/core/images/steps/create-volume/create-volume.help.html openstack_dashboard/static/app/core/images/steps/create-volume/create-volume.html openstack_dashboard/static/app/core/images/table/images-table.html openstack_dashboard/static/app/core/images/table/images.controller.js openstack_dashboard/static/app/core/images/table/images.controller.spec.js openstack_dashboard/static/app/core/images/workflows/create-volume.service.js openstack_dashboard/static/app/core/images/workflows/create-volume.service.spec.js openstack_dashboard/static/app/core/metadata/metadata.module.js openstack_dashboard/static/app/core/metadata/metadata.module.spec.js openstack_dashboard/static/app/core/metadata/metadata.service.js openstack_dashboard/static/app/core/metadata/metadata.service.spec.js openstack_dashboard/static/app/core/metadata/modal/modal-helper.controller.js openstack_dashboard/static/app/core/metadata/modal/modal-helper.controller.spec.js openstack_dashboard/static/app/core/metadata/modal/modal.controller.js openstack_dashboard/static/app/core/metadata/modal/modal.controller.spec.js openstack_dashboard/static/app/core/metadata/modal/modal.html openstack_dashboard/static/app/core/metadata/modal/modal.module.js openstack_dashboard/static/app/core/metadata/modal/modal.module.spec.js openstack_dashboard/static/app/core/metadata/modal/modal.service.js openstack_dashboard/static/app/core/metadata/modal/modal.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/cinder-extensions.service.js openstack_dashboard/static/app/core/openstack-service-api/cinder-extensions.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/cinder.service.js openstack_dashboard/static/app/core/openstack-service-api/cinder.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/common-test.mock.js openstack_dashboard/static/app/core/openstack-service-api/extensions.service.js openstack_dashboard/static/app/core/openstack-service-api/extensions.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/glance.service.js openstack_dashboard/static/app/core/openstack-service-api/glance.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/heat.service.js openstack_dashboard/static/app/core/openstack-service-api/heat.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/keypair-download.service.js openstack_dashboard/static/app/core/openstack-service-api/keypair-download.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/keystone.service.js openstack_dashboard/static/app/core/openstack-service-api/keystone.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/network.service.js openstack_dashboard/static/app/core/openstack-service-api/network.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/neutron-extensions.service.js openstack_dashboard/static/app/core/openstack-service-api/neutron-extensions.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/neutron.service.js openstack_dashboard/static/app/core/openstack-service-api/neutron.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/nova-extensions.service.js openstack_dashboard/static/app/core/openstack-service-api/nova-extensions.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/nova.service.js openstack_dashboard/static/app/core/openstack-service-api/nova.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/openstack-service-api.module.js openstack_dashboard/static/app/core/openstack-service-api/policy.service.js openstack_dashboard/static/app/core/openstack-service-api/policy.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/security-group.service.js openstack_dashboard/static/app/core/openstack-service-api/security-group.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/service-catalog.service.js openstack_dashboard/static/app/core/openstack-service-api/service-catalog.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/settings.service.js openstack_dashboard/static/app/core/openstack-service-api/settings.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/swift.service.js openstack_dashboard/static/app/core/openstack-service-api/swift.service.spec.js openstack_dashboard/static/app/core/openstack-service-api/user-session.service.js openstack_dashboard/static/app/core/openstack-service-api/user-session.service.spec.js openstack_dashboard/static/app/core/workflow/decorator.service.js openstack_dashboard/static/app/core/workflow/decorator.service.spec.js openstack_dashboard/static/app/core/workflow/workflow.module.js openstack_dashboard/static/app/core/workflow/workflow.module.spec.js openstack_dashboard/static/app/core/workflow/workflow.service.js openstack_dashboard/static/app/tech-debt/hz-namespace-resource-type-form.controller.js openstack_dashboard/static/app/tech-debt/hz-namespace-resource-type-form.controller.spec.js openstack_dashboard/static/app/tech-debt/image-form.controller.js openstack_dashboard/static/app/tech-debt/image-form.controller.spec.js openstack_dashboard/static/app/tech-debt/tech-debt.module.js openstack_dashboard/static/app/tech-debt/tech-debt.module.spec.js openstack_dashboard/static/dashboard/img/alarm-gray.gif openstack_dashboard/static/dashboard/img/alarm-gray.svg openstack_dashboard/static/dashboard/img/alarm-green.svg openstack_dashboard/static/dashboard/img/alarm-red.svg openstack_dashboard/static/dashboard/img/config-gray.gif openstack_dashboard/static/dashboard/img/config-gray.svg openstack_dashboard/static/dashboard/img/config-green.svg openstack_dashboard/static/dashboard/img/config-red.svg openstack_dashboard/static/dashboard/img/db-gray.gif openstack_dashboard/static/dashboard/img/db-gray.svg openstack_dashboard/static/dashboard/img/db-green.svg openstack_dashboard/static/dashboard/img/db-red.svg openstack_dashboard/static/dashboard/img/drag.png openstack_dashboard/static/dashboard/img/favicon.ico openstack_dashboard/static/dashboard/img/firewall-gray.gif openstack_dashboard/static/dashboard/img/firewall-gray.svg openstack_dashboard/static/dashboard/img/firewall-green.svg openstack_dashboard/static/dashboard/img/firewall-red.svg openstack_dashboard/static/dashboard/img/flavor-gray.gif openstack_dashboard/static/dashboard/img/flavor-gray.svg openstack_dashboard/static/dashboard/img/flavor-green.svg openstack_dashboard/static/dashboard/img/flavor-red.svg openstack_dashboard/static/dashboard/img/floatingip-gray.gif openstack_dashboard/static/dashboard/img/floatingip-gray.svg openstack_dashboard/static/dashboard/img/floatingip-green.svg openstack_dashboard/static/dashboard/img/floatingip-red.svg openstack_dashboard/static/dashboard/img/image-gray.gif openstack_dashboard/static/dashboard/img/image-gray.svg openstack_dashboard/static/dashboard/img/image-green.svg openstack_dashboard/static/dashboard/img/image-red.svg openstack_dashboard/static/dashboard/img/keypair-gray.gif openstack_dashboard/static/dashboard/img/keypair-gray.svg openstack_dashboard/static/dashboard/img/keypair-green.svg openstack_dashboard/static/dashboard/img/keypair-red.svg openstack_dashboard/static/dashboard/img/lb-gray.gif openstack_dashboard/static/dashboard/img/lb-gray.svg openstack_dashboard/static/dashboard/img/lb-green.svg openstack_dashboard/static/dashboard/img/lb-red.svg openstack_dashboard/static/dashboard/img/logo-splash.png openstack_dashboard/static/dashboard/img/logo-splash.svg openstack_dashboard/static/dashboard/img/logo.png openstack_dashboard/static/dashboard/img/logo.svg openstack_dashboard/static/dashboard/img/network-gray.gif openstack_dashboard/static/dashboard/img/network-gray.svg openstack_dashboard/static/dashboard/img/network-green.svg openstack_dashboard/static/dashboard/img/network-red.svg openstack_dashboard/static/dashboard/img/policy-gray.gif openstack_dashboard/static/dashboard/img/policy-gray.svg openstack_dashboard/static/dashboard/img/policy-green.svg openstack_dashboard/static/dashboard/img/policy-red.svg openstack_dashboard/static/dashboard/img/port-gray.gif openstack_dashboard/static/dashboard/img/port-gray.svg openstack_dashboard/static/dashboard/img/port-green.svg openstack_dashboard/static/dashboard/img/port-red.svg openstack_dashboard/static/dashboard/img/router-gray.gif openstack_dashboard/static/dashboard/img/router-gray.svg openstack_dashboard/static/dashboard/img/router-green.svg openstack_dashboard/static/dashboard/img/router-red.svg openstack_dashboard/static/dashboard/img/securitygroup-gray.gif openstack_dashboard/static/dashboard/img/securitygroup-gray.svg openstack_dashboard/static/dashboard/img/securitygroup-green.svg openstack_dashboard/static/dashboard/img/securitygroup-red.svg openstack_dashboard/static/dashboard/img/server-gray.gif openstack_dashboard/static/dashboard/img/server-gray.svg openstack_dashboard/static/dashboard/img/server-green.svg openstack_dashboard/static/dashboard/img/server-red.svg openstack_dashboard/static/dashboard/img/spinner.gif openstack_dashboard/static/dashboard/img/stack-gray.gif openstack_dashboard/static/dashboard/img/stack-gray.svg openstack_dashboard/static/dashboard/img/stack-green.svg openstack_dashboard/static/dashboard/img/stack-red.svg openstack_dashboard/static/dashboard/img/unknown-gray.gif openstack_dashboard/static/dashboard/img/unknown-gray.svg openstack_dashboard/static/dashboard/img/unknown-green.svg openstack_dashboard/static/dashboard/img/unknown-red.svg openstack_dashboard/static/dashboard/img/volume-gray.gif openstack_dashboard/static/dashboard/img/volume-gray.svg openstack_dashboard/static/dashboard/img/volume-green.svg openstack_dashboard/static/dashboard/img/volume-red.svg openstack_dashboard/static/dashboard/img/vpn-gray.svg openstack_dashboard/static/dashboard/img/vpn-green.svg openstack_dashboard/static/dashboard/img/vpn-red.svg openstack_dashboard/static/dashboard/img/vpn.gif openstack_dashboard/static/dashboard/img/wait-gray.gif openstack_dashboard/static/dashboard/img/wait-gray.svg openstack_dashboard/static/dashboard/img/wait-green.svg openstack_dashboard/static/dashboard/img/wait-red.svg openstack_dashboard/static/dashboard/scss/_contrib.scss openstack_dashboard/static/dashboard/scss/_debt.scss openstack_dashboard/static/dashboard/scss/_layout.scss openstack_dashboard/static/dashboard/scss/_legacy.scss openstack_dashboard/static/dashboard/scss/_mixins.scss openstack_dashboard/static/dashboard/scss/_util.scss openstack_dashboard/static/dashboard/scss/_variables.scss openstack_dashboard/static/dashboard/scss/horizon.scss openstack_dashboard/static/dashboard/scss/serial_console.scss openstack_dashboard/static/dashboard/scss/components/_bar_charts.scss openstack_dashboard/static/dashboard/scss/components/_charts.scss openstack_dashboard/static/dashboard/scss/components/_checkboxes.scss openstack_dashboard/static/dashboard/scss/components/_datepicker.scss openstack_dashboard/static/dashboard/scss/components/_forms.scss openstack_dashboard/static/dashboard/scss/components/_help_panel.scss openstack_dashboard/static/dashboard/scss/components/_icons.scss openstack_dashboard/static/dashboard/scss/components/_inline_edit.scss openstack_dashboard/static/dashboard/scss/components/_login.scss openstack_dashboard/static/dashboard/scss/components/_membership.scss openstack_dashboard/static/dashboard/scss/components/_messages.scss openstack_dashboard/static/dashboard/scss/components/_modals.scss openstack_dashboard/static/dashboard/scss/components/_navbar.scss openstack_dashboard/static/dashboard/scss/components/_network_topology.scss openstack_dashboard/static/dashboard/scss/components/_pending_bar.scss openstack_dashboard/static/dashboard/scss/components/_pie_charts.scss openstack_dashboard/static/dashboard/scss/components/_quota.scss openstack_dashboard/static/dashboard/scss/components/_resource_browser.scss openstack_dashboard/static/dashboard/scss/components/_resource_topology.scss openstack_dashboard/static/dashboard/scss/components/_selection_menu.scss openstack_dashboard/static/dashboard/scss/components/_sidebar.scss openstack_dashboard/static/dashboard/scss/components/_table_actions.scss openstack_dashboard/static/dashboard/scss/components/_tables.scss openstack_dashboard/static/dashboard/scss/components/_transfer_tables.scss openstack_dashboard/static/dashboard/scss/components/_wizard.scss openstack_dashboard/templates/403.html openstack_dashboard/templates/404.html openstack_dashboard/templates/500.html openstack_dashboard/templates/_footer.html openstack_dashboard/templates/_login_footer.html openstack_dashboard/templates/_stylesheets.html openstack_dashboard/templates/base.html openstack_dashboard/templates/context_selection/_anti_clickjack.html openstack_dashboard/templates/context_selection/_domain_list.html openstack_dashboard/templates/context_selection/_overview.html openstack_dashboard/templates/context_selection/_project_list.html openstack_dashboard/templates/context_selection/_region_list.html openstack_dashboard/templates/header/_brand.html openstack_dashboard/templates/header/_context_selection.html openstack_dashboard/templates/header/_header.html openstack_dashboard/templates/header/_region_selection.html openstack_dashboard/templates/header/_theme_list.html openstack_dashboard/templates/header/_user_menu.html openstack_dashboard/templates/horizon/_conf.html openstack_dashboard/templates/horizon/_custom_head_js.html openstack_dashboard/templates/horizon/_custom_meta.html openstack_dashboard/templates/horizon/_script_i18n.html openstack_dashboard/templates/horizon/_scripts.html openstack_dashboard/templates/themes/themes.scss openstack_dashboard/templatetags/__init__.py openstack_dashboard/templatetags/context_selection.py openstack_dashboard/templatetags/themes.py openstack_dashboard/test/__init__.py openstack_dashboard/test/error_pages_urls.py openstack_dashboard/test/helpers.py openstack_dashboard/test/settings.py openstack_dashboard/test/urls.py openstack_dashboard/test/api_tests/__init__.py openstack_dashboard/test/api_tests/base_tests.py openstack_dashboard/test/api_tests/ceilometer_tests.py openstack_dashboard/test/api_tests/cinder_rest_tests.py openstack_dashboard/test/api_tests/cinder_tests.py openstack_dashboard/test/api_tests/config_rest_tests.py openstack_dashboard/test/api_tests/fwaas_tests.py openstack_dashboard/test/api_tests/glance_rest_tests.py openstack_dashboard/test/api_tests/glance_tests.py openstack_dashboard/test/api_tests/heat_rest_tests.py openstack_dashboard/test/api_tests/heat_tests.py openstack_dashboard/test/api_tests/keystone_rest_tests.py openstack_dashboard/test/api_tests/keystone_tests.py openstack_dashboard/test/api_tests/lbaas_tests.py openstack_dashboard/test/api_tests/network_rest_tests.py openstack_dashboard/test/api_tests/network_tests.py openstack_dashboard/test/api_tests/neutron_rest_tests.py openstack_dashboard/test/api_tests/neutron_tests.py openstack_dashboard/test/api_tests/nova_rest_tests.py openstack_dashboard/test/api_tests/nova_tests.py openstack_dashboard/test/api_tests/policy_rest_tests.py openstack_dashboard/test/api_tests/rest_util_tests.py openstack_dashboard/test/api_tests/swift_rest_tests.py openstack_dashboard/test/api_tests/swift_tests.py openstack_dashboard/test/api_tests/vpnaas_tests.py openstack_dashboard/test/integration_tests/README.rst openstack_dashboard/test/integration_tests/__init__.py openstack_dashboard/test/integration_tests/basewebobject.py openstack_dashboard/test/integration_tests/config.py openstack_dashboard/test/integration_tests/decorators.py openstack_dashboard/test/integration_tests/helpers.py openstack_dashboard/test/integration_tests/horizon.conf openstack_dashboard/test/integration_tests/pages/__init__.py openstack_dashboard/test/integration_tests/pages/basepage.py openstack_dashboard/test/integration_tests/pages/loginpage.py openstack_dashboard/test/integration_tests/pages/navigation.py openstack_dashboard/test/integration_tests/pages/pageobject.py openstack_dashboard/test/integration_tests/pages/admin/__init__.py openstack_dashboard/test/integration_tests/pages/admin/system/__init__.py openstack_dashboard/test/integration_tests/pages/admin/system/flavorspage.py openstack_dashboard/test/integration_tests/pages/admin/system/hostaggregatespage.py openstack_dashboard/test/integration_tests/pages/admin/system/imagespage.py openstack_dashboard/test/integration_tests/pages/admin/system/metadatadefinitionspage.py openstack_dashboard/test/integration_tests/pages/admin/system/overviewpage.py openstack_dashboard/test/integration_tests/pages/admin/system/resource_usage/__init__.py openstack_dashboard/test/integration_tests/pages/admin/system/system_info/__init__.py openstack_dashboard/test/integration_tests/pages/admin/system/volumes/__init__.py openstack_dashboard/test/integration_tests/pages/admin/system/volumes/volumesnapshotspage.py openstack_dashboard/test/integration_tests/pages/admin/system/volumes/volumespage.py openstack_dashboard/test/integration_tests/pages/admin/system/volumes/volumetypespage.py openstack_dashboard/test/integration_tests/pages/identity/__init__.py openstack_dashboard/test/integration_tests/pages/identity/projectspage.py openstack_dashboard/test/integration_tests/pages/identity/userspage.py openstack_dashboard/test/integration_tests/pages/project/__init__.py openstack_dashboard/test/integration_tests/pages/project/compute/__init__.py openstack_dashboard/test/integration_tests/pages/project/compute/imagespage.py openstack_dashboard/test/integration_tests/pages/project/compute/instancespage.py openstack_dashboard/test/integration_tests/pages/project/compute/overviewpage.py openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/__init__.py openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/floatingipspage.py openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/keypairspage.py openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/securitygroupspage.py openstack_dashboard/test/integration_tests/pages/project/compute/volumes/__init__.py openstack_dashboard/test/integration_tests/pages/project/compute/volumes/volumesnapshotspage.py openstack_dashboard/test/integration_tests/pages/project/compute/volumes/volumespage.py openstack_dashboard/test/integration_tests/pages/project/network/__init__.py openstack_dashboard/test/integration_tests/pages/project/network/networkspage.py openstack_dashboard/test/integration_tests/pages/project/network/routerspage.py openstack_dashboard/test/integration_tests/pages/project/object_store/__init__.py openstack_dashboard/test/integration_tests/pages/project/orchestration/__init__.py openstack_dashboard/test/integration_tests/pages/settings/__init__.py openstack_dashboard/test/integration_tests/pages/settings/changepasswordpage.py openstack_dashboard/test/integration_tests/pages/settings/usersettingspage.py openstack_dashboard/test/integration_tests/regions/__init__.py openstack_dashboard/test/integration_tests/regions/bars.py openstack_dashboard/test/integration_tests/regions/baseregion.py openstack_dashboard/test/integration_tests/regions/exceptions.py openstack_dashboard/test/integration_tests/regions/forms.py openstack_dashboard/test/integration_tests/regions/menus.py openstack_dashboard/test/integration_tests/regions/messages.py openstack_dashboard/test/integration_tests/regions/tables.py openstack_dashboard/test/integration_tests/tests/__init__.py openstack_dashboard/test/integration_tests/tests/test_flavors.py openstack_dashboard/test/integration_tests/tests/test_floatingips.py openstack_dashboard/test/integration_tests/tests/test_host_aggregates.py openstack_dashboard/test/integration_tests/tests/test_images.py openstack_dashboard/test/integration_tests/tests/test_instances.py openstack_dashboard/test/integration_tests/tests/test_keypairs.py openstack_dashboard/test/integration_tests/tests/test_login.py openstack_dashboard/test/integration_tests/tests/test_metadata_definitions.py openstack_dashboard/test/integration_tests/tests/test_networks.py openstack_dashboard/test/integration_tests/tests/test_projects.py openstack_dashboard/test/integration_tests/tests/test_router.py openstack_dashboard/test/integration_tests/tests/test_security_groups.py openstack_dashboard/test/integration_tests/tests/test_user_settings.py openstack_dashboard/test/integration_tests/tests/test_users.py openstack_dashboard/test/integration_tests/tests/test_volume_snapshots.py openstack_dashboard/test/integration_tests/tests/test_volumes.py openstack_dashboard/test/integration_tests/tests/test_volumetypes.py openstack_dashboard/test/integration_tests/tests/test-data/empty_namespace.json openstack_dashboard/test/jasmine/__init__.py openstack_dashboard/test/jasmine/jasmine.py openstack_dashboard/test/templates/404.html openstack_dashboard/test/templates/500.html openstack_dashboard/test/templates/_tab.html openstack_dashboard/test/templates/base-sidebar.html openstack_dashboard/test/templates/tab_group.html openstack_dashboard/test/templates/workflow.html openstack_dashboard/test/templates/registration/login.html openstack_dashboard/test/test_data/__init__.py openstack_dashboard/test/test_data/ceilometer_data.py openstack_dashboard/test/test_data/cinder_data.py openstack_dashboard/test/test_data/exceptions.py openstack_dashboard/test/test_data/glance_data.py openstack_dashboard/test/test_data/heat_data.py openstack_dashboard/test/test_data/keystone_data.py openstack_dashboard/test/test_data/neutron_data.py openstack_dashboard/test/test_data/nova_data.py openstack_dashboard/test/test_data/swift_data.py openstack_dashboard/test/test_data/utils.py openstack_dashboard/test/test_panels/__init__.py openstack_dashboard/test/test_panels/another_panel/__init__.py openstack_dashboard/test/test_panels/another_panel/panel.py openstack_dashboard/test/test_panels/another_panel/urls.py openstack_dashboard/test/test_panels/another_panel/views.py openstack_dashboard/test/test_panels/another_panel/templates/another_panel/index.html openstack_dashboard/test/test_panels/nonloading_panel/__init__.py openstack_dashboard/test/test_panels/nonloading_panel/panel.py openstack_dashboard/test/test_panels/nonloading_panel/urls.py openstack_dashboard/test/test_panels/nonloading_panel/views.py openstack_dashboard/test/test_panels/nonloading_panel/templates/nonloading_panel/index.html openstack_dashboard/test/test_panels/plugin_panel/__init__.py openstack_dashboard/test/test_panels/plugin_panel/panel.py openstack_dashboard/test/test_panels/plugin_panel/urls.py openstack_dashboard/test/test_panels/plugin_panel/views.py openstack_dashboard/test/test_panels/plugin_panel/static/plugin_panel/plugin.scss openstack_dashboard/test/test_panels/plugin_panel/static/plugin_panel/plugin.spec.js openstack_dashboard/test/test_panels/plugin_panel/static/plugin_panel/plugin_module.js openstack_dashboard/test/test_panels/plugin_panel/templates/plugin_panel/index.html openstack_dashboard/test/test_panels/second_panel/__init__.py openstack_dashboard/test/test_panels/second_panel/panel.py openstack_dashboard/test/test_panels/second_panel/urls.py openstack_dashboard/test/test_panels/second_panel/views.py openstack_dashboard/test/test_panels/second_panel/templates/second_panel/index.html openstack_dashboard/test/test_plugins/__init__.py openstack_dashboard/test/test_plugins/panel_group_tests.py openstack_dashboard/test/test_plugins/panel_tests.py openstack_dashboard/test/test_plugins/panel_config/_10_admin_add_panel.py openstack_dashboard/test/test_plugins/panel_config/_20_admin_remove_panel.py openstack_dashboard/test/test_plugins/panel_config/_30_admin_default_panel.py openstack_dashboard/test/test_plugins/panel_config/_40_admin_nonloading_panel.py openstack_dashboard/test/test_plugins/panel_config/__init__.py openstack_dashboard/test/test_plugins/panel_group_config/_10_admin_add_panel_group.py openstack_dashboard/test/test_plugins/panel_group_config/_20_admin_add_panel_to_group.py openstack_dashboard/test/test_plugins/panel_group_config/_30_admin_add_second_panel_group.py openstack_dashboard/test/test_plugins/panel_group_config/_40_admin_add_panel_to_second_group.py openstack_dashboard/test/test_plugins/panel_group_config/_50_admin_add_panel_to_default_group.py openstack_dashboard/test/test_plugins/panel_group_config/__init__.py openstack_dashboard/test/tests/__init__.py openstack_dashboard/test/tests/error_pages.py openstack_dashboard/test/tests/policy.py openstack_dashboard/test/tests/policy_backend.py openstack_dashboard/test/tests/quotas.py openstack_dashboard/test/tests/selenium_tests.py openstack_dashboard/test/tests/templates.py openstack_dashboard/test/tests/utils.py openstack_dashboard/themes/default/_styles.scss openstack_dashboard/themes/default/_variables.scss openstack_dashboard/themes/default/bootstrap/_styles.scss openstack_dashboard/themes/default/bootstrap/_variables.scss openstack_dashboard/themes/default/bootstrap/components/_dropdowns.scss openstack_dashboard/themes/default/bootstrap/components/_forms.scss openstack_dashboard/themes/default/bootstrap/components/_navbar.scss openstack_dashboard/themes/default/bootstrap/components/_navs.scss openstack_dashboard/themes/default/bootstrap/components/_type.scss openstack_dashboard/themes/default/horizon/_styles.scss openstack_dashboard/themes/default/horizon/_variables.scss openstack_dashboard/themes/default/horizon/components/_breadcrumb_header.scss openstack_dashboard/themes/default/horizon/components/_context_selection.scss openstack_dashboard/themes/default/horizon/components/_login.scss openstack_dashboard/themes/default/horizon/components/_messages.scss openstack_dashboard/themes/default/horizon/components/_navbar.scss openstack_dashboard/themes/default/horizon/components/_pie_charts.scss openstack_dashboard/themes/default/horizon/components/_quota.scss openstack_dashboard/themes/default/horizon/components/_sidebar.scss openstack_dashboard/themes/default/horizon/components/_table_actions.scss openstack_dashboard/themes/default/horizon/components/_tables.scss openstack_dashboard/themes/material/static/_styles.scss openstack_dashboard/themes/material/static/_variables.scss openstack_dashboard/themes/material/static/bootstrap/_styles.scss openstack_dashboard/themes/material/static/bootstrap/_variable_customizations.scss openstack_dashboard/themes/material/static/bootstrap/_variables.scss openstack_dashboard/themes/material/static/horizon/_icons.scss openstack_dashboard/themes/material/static/horizon/_styles.scss openstack_dashboard/themes/material/static/horizon/_variables.scss openstack_dashboard/themes/material/static/horizon/components/_checkboxes.scss openstack_dashboard/themes/material/static/horizon/components/_context_selection.scss openstack_dashboard/themes/material/static/horizon/components/_hamburger.scss openstack_dashboard/themes/material/static/horizon/components/_help_panel.scss openstack_dashboard/themes/material/static/horizon/components/_magic_search.scss openstack_dashboard/themes/material/static/horizon/components/_messages.scss openstack_dashboard/themes/material/static/horizon/components/_navbar.scss openstack_dashboard/themes/material/static/horizon/components/_sidebar.scss openstack_dashboard/themes/material/static/js/material.hamburger.js openstack_dashboard/themes/material/templates/auth/_splash.html openstack_dashboard/themes/material/templates/header/_brand.html openstack_dashboard/themes/material/templates/header/_header.html openstack_dashboard/themes/material/templates/horizon/_sidebar.html openstack_dashboard/themes/material/templates/material/openstack-one-color-alt.svg openstack_dashboard/themes/material/templates/material/openstack-one-color-alt.svg.old openstack_dashboard/themes/material/templates/material/openstack-one-color.svg openstack_dashboard/usage/__init__.py openstack_dashboard/usage/base.py openstack_dashboard/usage/quotas.py openstack_dashboard/usage/tables.py openstack_dashboard/usage/views.py openstack_dashboard/utils/__init__.py openstack_dashboard/utils/filters.py openstack_dashboard/utils/identity.py openstack_dashboard/utils/metering.py openstack_dashboard/utils/settings.py openstack_dashboard/wsgi/django.wsgi releasenotes/notes/.placeholder releasenotes/notes/bp-add-server-metadata-a5d5582966ef25c5.yaml releasenotes/notes/bp-allow-launching-ports-b1fcc495777b7f4c.yaml releasenotes/notes/bp-angular-performance-strict-di-3cf325d8bfca8487.yaml releasenotes/notes/bp-angularize-swift-9a1b44aa3646bc8c.yaml releasenotes/notes/bp-bootstrap-theme-preview-65da171c9b943646.yaml releasenotes/notes/bp-cache-templates-4ab00dcda195a03a.yaml releasenotes/notes/bp-cinder-consistency-groups-b0aba555b1ed4a6c.yaml releasenotes/notes/bp-edit-server-metadata-7e6b00946a2e793a.yaml releasenotes/notes/bp-enable-angular-launch-instance-897f7bb227711c86.yaml releasenotes/notes/bp-integrate-magic-search-03a97d4431d7c3d1.yaml releasenotes/notes/bp-integration-tests-hardening-8e94e87bc548c1fe.yaml releasenotes/notes/bp-integration-with-cinder-volume-encryption-80a3fe4ff66314b2.yaml releasenotes/notes/bp-keystone-federation-protocol-mapping-a6ea9f7c35d2d6f0.yaml releasenotes/notes/bp-local-settings-override-mechanism-6c8632432cf1f44c.yaml releasenotes/notes/bp-pagination-for-flavor-f603fd7630e13756.yaml releasenotes/notes/deprecation-of-default-subnet-pool-label-options-b05ebccbf6f68ecf.yaml releasenotes/notes/domains-0581aa42773d5f41.yaml releasenotes/notes/dynamic-themes-b6b02238e47b99f8.yaml releasenotes/notes/enable-js-catalog-plugin-1885df911148247a.yaml releasenotes/notes/enable-phantomjs-selenium-cce59f25cb327ca2.yaml releasenotes/notes/excise-sahara-7eff95feb416ce4b.yaml releasenotes/notes/excise-trove-ce576b50fbcd15ad.yaml releasenotes/notes/extensible-service-a8689c89a71f8961.yaml releasenotes/notes/gb-to-gib-conversion-8a91839030a2f570.yaml releasenotes/notes/hz-select-fixes-c9bfe6a53e0daa20.yaml releasenotes/notes/keystone-federation-idp-d4456dd3b3081a53.yaml releasenotes/notes/launch-instance-defaults-c6ab65b7ab822162.yaml releasenotes/notes/message-of-the-day-19eb745a147ca56d.yaml releasenotes/notes/move-policy-engine-b19e434a62912e5f.yaml releasenotes/notes/removal-of-webroot-theme-108db1d2f11da449.yaml releasenotes/notes/workflow-step-policy-1ca99b0249294337.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/liberty.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder tools/abandon_old_reviews.sh tools/install_venv.py tools/install_venv_common.py tools/pseudo.py tools/with_venv.sh tools/gate/integration/devstack_exports.sh tools/gate/integration/post_test_hook.sh tools/gate/integration/pre_test_hook.shhorizon-9.0.0/horizon.egg-info/PKG-INFO0000664000567000056710000000565012701407227020640 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: horizon Version: 9.0.0 Summary: OpenStack Dashboard Home-page: http://www.openstack.org/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description: ============================= Horizon (OpenStack Dashboard) ============================= Horizon is a Django-based project aimed at providing a complete OpenStack Dashboard along with an extensible framework for building new dashboards from reusable components. The ``openstack_dashboard`` module is a reference implementation of a Django site that uses the ``horizon`` app to provide web-based interactions with the various OpenStack projects. * Release management: https://launchpad.net/horizon * Blueprints and feature specifications: https://blueprints.launchpad.net/horizon * Issue tracking: https://bugs.launchpad.net/horizon Using Horizon ============= See ``doc/source/topics/install.rst`` about how to install Horizon in your OpenStack setup. It describes the example steps and has pointers for more detailed settings and configurations. It is also available at http://docs.openstack.org/developer/horizon/topics/install.html. Getting Started for Developers ============================== ``doc/source/quickstart.rst`` or http://docs.openstack.org/developer/horizon/quickstart.html describes how to setup Horizon development environment and start development. Building Contributor Documentation ================================== This documentation is written by contributors, for contributors. The source is maintained in the ``doc/source`` directory using `reStructuredText`_ and built by `Sphinx`_ .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _Sphinx: http://sphinx-doc.org/ * Building Automatically:: $ ./run_tests.sh --docs * Building Manually:: $ tools/with_venv.sh sphinx-build doc/source doc/build/html Results are in the ``doc/build/html`` directory Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: OpenStack Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Topic :: Internet :: WWW/HTTP horizon-9.0.0/horizon.egg-info/top_level.txt0000664000567000056710000000003412701407227022264 0ustar jenkinsjenkins00000000000000horizon openstack_dashboard horizon-9.0.0/horizon.egg-info/pbr.json0000664000567000056710000000005612701407227021214 0ustar jenkinsjenkins00000000000000{"is_release": true, "git_version": "2eb320b"}horizon-9.0.0/setup.cfg0000664000567000056710000000172612701407231016175 0ustar jenkinsjenkins00000000000000[metadata] name = horizon summary = OpenStack Dashboard description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = http://www.openstack.org/ classifier = Development Status :: 5 - Production/Stable Environment :: OpenStack Framework :: Django Intended Audience :: Developers Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: OS Independent Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Topic :: Internet :: WWW/HTTP [global] setup-hooks = openstack_dashboard.hooks.setup_hook [files] packages = horizon openstack_dashboard [build_sphinx] all_files = 1 build-dir = doc/build source-dir = doc/source [nosetests] verbosity = 2 detailed-errors = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 horizon-9.0.0/manage.py0000775000567000056710000000150612701407063016160 0ustar jenkinsjenkins00000000000000#!/usr/bin/env python # 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. import os import sys from django.core.management import execute_from_command_line # noqa if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openstack_dashboard.settings") execute_from_command_line(sys.argv) horizon-9.0.0/.eslintrc0000664000567000056710000000244112701407071016175 0ustar jenkinsjenkins00000000000000# Set up globals globals: angular: false extends: openstack # Most environment options are not explicitly enabled or disabled, only # included here for completeness' sake. They are commented out, because the # global updates.py script would otherwise override them during a global # requirements synchronization. # # Individual projects should choose which platforms they deploy to. env: # browser global variables. browser: true # Adds all of the Jasmine testing global variables for version 1.3 and 2.0. jasmine: true # Below we adjust rules specific to horizon's usage of openstack's linting # rules, and its own plugin inclusions. rules: ############################################################################# # Disabled Rules from eslint-config-openstack ############################################################################# valid-jsdoc: 1 no-undefined: 1 brace-style: 1 no-extra-parens: 1 callback-return: 1 block-scoped-var: 1 ############################################################################# # Angular Plugin Customization ############################################################################# angular/controller-as-vm: - 1 - "ctrl" # Remove after migrating to angular 1.4 or later. angular/no-cookiestore: - 1 horizon-9.0.0/tools/0000775000567000056710000000000012701407231015506 5ustar jenkinsjenkins00000000000000horizon-9.0.0/tools/install_venv_common.py0000664000567000056710000001350612701407063022144 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2013 IBM Corp. # # 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. """Provides methods needed by installation script for OpenStack development virtual environments. Since this script is used to bootstrap a virtualenv from the system's Python environment, it should be kept strictly compatible with Python 2.6. Synced in from openstack-common """ from __future__ import print_function import optparse import os import subprocess import sys class InstallVenv(object): def __init__(self, root, venv, requirements, test_requirements, py_version, project): self.root = root self.venv = venv self.requirements = requirements self.test_requirements = test_requirements self.py_version = py_version self.project = project def die(self, message, *args): print(message % args, file=sys.stderr) sys.exit(1) def check_python_version(self): if sys.version_info < (2, 6): self.die("Need Python Version >= 2.6") def run_command_with_code(self, cmd, redirect_output=True, check_exit_code=True): """Runs a command in an out-of-process shell. Returns the output of that command. Working directory is self.root. """ if redirect_output: stdout = subprocess.PIPE else: stdout = None proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) output = proc.communicate()[0] if check_exit_code and proc.returncode != 0: self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) return (output, proc.returncode) def run_command(self, cmd, redirect_output=True, check_exit_code=True): return self.run_command_with_code(cmd, redirect_output, check_exit_code)[0] def get_distro(self): if (os.path.exists('/etc/fedora-release') or os.path.exists('/etc/redhat-release')): return Fedora( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) else: return Distro( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) def check_dependencies(self): self.get_distro().install_virtualenv() def create_virtualenv(self, no_site_packages=True): """Creates the virtual environment and installs PIP. Creates the virtual environment and installs PIP only into the virtual environment. """ if not os.path.isdir(self.venv): print('Creating venv...', end=' ') if no_site_packages: self.run_command(['virtualenv', '-q', '--no-site-packages', self.venv]) else: self.run_command(['virtualenv', '-q', self.venv]) print('done.') else: print("venv already exists...") pass def pip_install(self, *args): self.run_command(['tools/with_venv.sh', 'pip', 'install', '--upgrade'] + list(args), redirect_output=False) def install_dependencies(self): print('Installing dependencies with pip (this can take a while)...') # First things first, make sure our venv has the latest pip and # setuptools and pbr self.pip_install('pip>=1.4') self.pip_install('setuptools') self.pip_install('pbr') self.pip_install('-r', self.requirements, '-r', self.test_requirements) def parse_args(self, argv): """Parses command-line arguments.""" parser = optparse.OptionParser() parser.add_option('-n', '--no-site-packages', action='store_true', help="Do not inherit packages from global Python " "install") return parser.parse_args(argv[1:])[0] class Distro(InstallVenv): def check_cmd(self, cmd): return bool(self.run_command(['which', cmd], check_exit_code=False).strip()) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if self.check_cmd('easy_install'): print('Installing virtualenv via easy_install...', end=' ') if self.run_command(['easy_install', 'virtualenv']): print('Succeeded') return else: print('Failed') self.die('ERROR: virtualenv not found.\n\n%s development' ' requires virtualenv, please install it using your' ' favorite package management tool' % self.project) class Fedora(Distro): """This covers all Fedora-based distributions. Includes: Fedora, RHEL, CentOS, Scientific Linux """ def check_pkg(self, pkg): return self.run_command_with_code(['rpm', '-q', pkg], check_exit_code=False)[1] == 0 def install_virtualenv(self): if self.check_cmd('virtualenv'): return if not self.check_pkg('python-virtualenv'): self.die("Please install 'python-virtualenv'.") super(Fedora, self).install_virtualenv() horizon-9.0.0/tools/install_venv.py0000664000567000056710000000454112701407063020573 0ustar jenkinsjenkins00000000000000# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2010 OpenStack Foundation # Copyright 2013 IBM Corp. # # 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. import os import sys import install_venv_common as install_venv # noqa def print_help(venv, root): help = """ OpenStack development environment setup is complete. OpenStack development uses virtualenv to track and manage Python dependencies while in development and testing. To activate the OpenStack virtualenv for the extent of your current shell session you can run: $ source %s/bin/activate Or, if you prefer, you can run commands in the virtualenv on a case by case basis by running: $ %s/tools/with_venv.sh Also, make test will automatically use the virtualenv. """ print(help % (venv, root)) def main(argv): root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) if os.environ.get('tools_path'): root = os.environ['tools_path'] venv = os.path.join(root, '.venv') if os.environ.get('venv'): venv = os.environ['venv'] pip_requires = os.path.join(root, 'requirements.txt') test_requires = os.path.join(root, 'test-requirements.txt') py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) project = 'OpenStack' install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, py_version, project) options = install.parse_args(argv) install.check_python_version() install.check_dependencies() install.create_virtualenv(no_site_packages=options.no_site_packages) install.install_dependencies() print_help(venv, root) if __name__ == '__main__': main(sys.argv) horizon-9.0.0/tools/pseudo.py0000775000567000056710000000506112701407063017367 0ustar jenkinsjenkins00000000000000#!/usr/bin/env python # coding: utf-8 # Copyright 2015 IBM Corp. # 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. import argparse import babel.messages.catalog as catalog import babel.messages.pofile as pofile def translate(segment): prefix = u"" # When the id starts with a newline the mo compiler enforces that # the translated message must also start with a newline. Make # sure that doesn't get broken when prepending the bracket. if segment.startswith('\n'): prefix = u"\n" orig_size = len(segment) # Add extra expansion space based on recommendation from # http://www-01.ibm.com/software/globalization/guidelines/a3.html if orig_size < 20: multiplier = 1 elif orig_size < 30: multiplier = 0.8 elif orig_size < 50: multiplier = 0.6 elif orig_size < 70: multiplier = 0.4 else: multiplier = 0.3 extra_length = int(max(0, (orig_size * multiplier) - 10)) extra_chars = "~" * extra_length return u"{0}[~{1}~您好Ñшçã‚{2}]".format(prefix, segment, extra_chars) def main(): # Check arguments parser = argparse.ArgumentParser() parser.add_argument('pot_filename', type=argparse.FileType('r')) parser.add_argument('po_filename', type=argparse.FileType('w')) parser.add_argument('locale') args = parser.parse_args() # read POT file pot_cat = pofile.read_po(args.pot_filename, ignore_obsolete=True) # Create the new Catalog new_cat = catalog.Catalog(locale=args.locale, last_translator="pseudo.py", charset="utf-8") num_plurals = new_cat.num_plurals # Process messages from template for msg in pot_cat: if msg.pluralizable: msg.string = [translate(u"{}:{}".format(i, msg.id[0])) for i in range(num_plurals)] else: msg.string = translate(msg.id) new_cat[msg.id] = msg # Write "translated" PO file pofile.write_po(args.po_filename, new_cat, ignore_obsolete=True) if __name__ == '__main__': main() horizon-9.0.0/tools/abandon_old_reviews.sh0000775000567000056710000000535512701407063022064 0ustar jenkinsjenkins00000000000000#!/bin/bash # # 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. # # # # before you run this modify your .ssh/config to create a # review.openstack.org entry: # # Host review.openstack.org # User # Port 29418 # # Note: due to gerrit bug somewhere, this double posts messages. :( # first purge the all reviews that are more than 4w old and blocked by a core -2 set -o errexit function abandon_review { local gitid=$1 shift local msg=$@ echo "Abandoning $gitid" # echo ssh review.openstack.org gerrit review $gitid --abandon --message \"$msg\" ssh review.openstack.org gerrit review $gitid --abandon --message \"$msg\" } PROJECTS="(project:openstack/horizon OR project:openstack/django_openstack_auth)" blocked_reviews=$(ssh review.openstack.org "gerrit query --current-patch-set --format json $PROJECTS status:open age:4w label:Code-Review<=-2" | jq .currentPatchSet.revision | grep -v null | sed 's/"//g') blocked_msg=$(cat < 4 weeks without comment and currently blocked by a core reviewer with a -2. We are abandoning this for now. Feel free to reactivate the review by pressing the restore button and contacting the reviewer with the -2 on this review to ensure you address their concerns. EOF ) # For testing, put in a git rev of something you own and uncomment # blocked_reviews="b6c4218ae4d75b86c33fa3d37c27bc23b46b6f0f" for review in $blocked_reviews; do # echo ssh review.openstack.org gerrit review $review --abandon --message \"$msg\" echo "Blocked review $review" abandon_review $review $blocked_msg done # then purge all the reviews that are > 4w with no changes and Jenkins has -1ed failing_reviews=$(ssh review.openstack.org "gerrit query --current-patch-set --format json $PROJECTS status:open age:4w NOT label:Verified>=1,jenkins" | jq .currentPatchSet.revision | grep -v null | sed 's/"//g') failing_msg=$(cat < 4 weeks without comment, and failed Jenkins the last time it was checked. We are abandoning this for now. Feel free to reactivate the review by pressing the restore button and leaving a 'recheck' comment to get fresh test results. EOF ) for review in $failing_reviews; do echo "Failing review $review" abandon_review $review $failing_msg done horizon-9.0.0/tools/with_venv.sh0000775000567000056710000000077212701407063020067 0ustar jenkinsjenkins00000000000000#!/bin/bash TOOLS_PATH=${TOOLS_PATH:-$(dirname $0)} VENV_PATH=${VENV_PATH:-${TOOLS_PATH}} VENV_DIR=${VENV_NAME:-/../.venv} TOOLS=${TOOLS_PATH} VENV=${VENV:-${VENV_PATH}/${VENV_DIR}} HORIZON_DIR=${TOOLS%/tools} # This horrible mangling of the PYTHONPATH is required to get the # babel-angular-gettext extractor to work. To fix this the extractor needs to # be packaged on pypi and added to global requirements. That work is in progress. export PYTHONPATH="$HORIZON_DIR" source ${VENV}/bin/activate && "$@" horizon-9.0.0/tools/gate/0000775000567000056710000000000012701407231016426 5ustar jenkinsjenkins00000000000000horizon-9.0.0/tools/gate/integration/0000775000567000056710000000000012701407231020751 5ustar jenkinsjenkins00000000000000horizon-9.0.0/tools/gate/integration/pre_test_hook.sh0000775000567000056710000000041712701407071024161 0ustar jenkinsjenkins00000000000000#!/bin/bash # This script will be executed inside pre_test_hook function in devstack gate cd /opt/stack/new/horizon/openstack_dashboard/local/local_settings.d mv _20_integration_tests_scaffolds.py.example _20_integration_tests_scaffolds.py sudo service apache2 restart horizon-9.0.0/tools/gate/integration/devstack_exports.sh0000664000567000056710000000030412701407063024675 0ustar jenkinsjenkins00000000000000export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TIMEOUT=90 export DEVSTACK_GATE_TEMPEST=0 export DEVSTACK_GATE_EXERCISES=0 export DEVSTACK_GATE_INSTALL_TESTONLY=1 export DEVSTACK_GATE_NEUTRON=1 horizon-9.0.0/tools/gate/integration/post_test_hook.sh0000775000567000056710000000063512701407071024362 0ustar jenkinsjenkins00000000000000#!/bin/bash # This script will be executed inside post_test_hook function in devstack gate cd /opt/stack/new/horizon sudo -H -u stack tox -e py27integration retval=$? if [ -d openstack_dashboard/test/integration_tests/integration_tests_screenshots/ ]; then cp -r openstack_dashboard/test/integration_tests/integration_tests_screenshots/ /home/jenkins/workspace/gate-horizon-dsvm-integration/ fi exit $retval horizon-9.0.0/package.json0000664000567000056710000000210212701407063016632 0ustar jenkinsjenkins00000000000000{ "version": "0.0.0", "private": true, "name": "horizon", "description": "OpenStack Horizon - Angular", "repository": "none", "license": "Apache 2.0", "devDependencies": { "eslint": "1.2.1", "eslint-config-openstack": "1.2.3", "eslint-plugin-angular": "0.15.0", "jasmine-core": "2.2.0", "karma": "0.12.31", "karma-chrome-launcher": "0.1.8", "karma-cli": "0.0.4", "karma-coverage": "0.3.1", "karma-jasmine": "0.3.5", "karma-ng-html2js-preprocessor": "0.1.2", "karma-phantomjs-launcher": "0.2.0", "karma-threshold-reporter": "0.1.15", "phantomjs": "1.9.17" }, "scripts": { "postinstall": "if [ ! -d .venv ]; then tox -epy27 --notest; fi", "test": "karma start horizon/karma.conf.js --single-run && karma start openstack_dashboard/karma.conf.js --single-run", "lint": "eslint --no-color openstack_dashboard/static horizon/static openstack_dashboard/dashboards/*/static", "lintq": "eslint --quiet openstack_dashboard/static horizon/static openstack_dashboard/dashboards/*/static" }, "dependencies": {} } horizon-9.0.0/doc/0000775000567000056710000000000012701407231015113 5ustar jenkinsjenkins00000000000000horizon-9.0.0/doc/source/0000775000567000056710000000000012701407231016413 5ustar jenkinsjenkins00000000000000horizon-9.0.0/doc/source/index.rst0000664000567000056710000000561312701407063020264 0ustar jenkinsjenkins00000000000000.. Copyright 2012 OpenStack Foundation All Rights Reserved. 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. ======================================== Horizon: The OpenStack Dashboard Project ======================================== Introduction ============ Horizon is the canonical implementation of `OpenStack's Dashboard `_, which provides a web based user interface to OpenStack services including Nova, Swift, Keystone, etc. For a more in-depth look at Horizon and its architecture, see the :doc:`Introduction to Horizon `. To learn what you need to know to get going, see the :doc:`quickstart`. Using Horizon ============= How to use Horizon in your own projects. .. toctree:: :maxdepth: 1 topics/install topics/deployment topics/settings topics/customizing topics/packaging Developer Docs ============== For those wishing to develop Horizon itself, or go in-depth with building your own :class:`~horizon.Dashboard` or :class:`~horizon.Panel` classes, the following documentation is provided. General information ------------------- Brief guides to areas of interest and importance when developing Horizon. .. toctree:: :maxdepth: 1 intro quickstart contributing testing plugin_registry Tutorials --------- Detailed tutorials to help you get started. .. toctree:: :maxdepth: 1 tutorials/plugin tutorials/dashboard Topic Guides ------------ Information on how to work with specific areas of Horizon can be found in the following topic guides. .. toctree:: :maxdepth: 1 topics/workflows topics/tables topics/policy topics/testing topics/table_actions topics/angularjs topics/javascript_testing topics/styling API Reference ------------- In-depth documentation for Horizon and its APIs. .. toctree:: :maxdepth: 1 ref/run_tests ref/horizon ref/workflows ref/tables ref/tabs ref/forms ref/middleware ref/context_processors ref/decorators ref/exceptions ref/test ref/local_conf Source Code Reference --------------------- Auto-generated reference for the complete source code. .. toctree:: :maxdepth: 1 sourcecode/autoindex Release Notes ============= .. toctree:: :glob: :maxdepth: 1 releases/* Information =========== .. toctree:: :maxdepth: 1 faq glossary * :ref:`genindex` * :ref:`modindex` horizon-9.0.0/doc/source/ref/0000775000567000056710000000000012701407231017167 5ustar jenkinsjenkins00000000000000horizon-9.0.0/doc/source/ref/context_processors.rst0000664000567000056710000000021312701407063023666 0ustar jenkinsjenkins00000000000000========================== Horizon Context Processors ========================== .. automodule:: horizon.context_processors :members: horizon-9.0.0/doc/source/ref/forms.rst0000664000567000056710000000621012701407063021051 0ustar jenkinsjenkins00000000000000============= Horizon Forms ============= Horizon ships with some very useful base form classes, form fields, class-based views, and javascript helpers which streamline most of the common tasks related to form handling. Form Classes ============ .. automodule:: horizon.forms.base :members: Form Fields =========== .. automodule:: horizon.forms.fields :members: Form Views ========== .. automodule:: horizon.forms.views :members: Forms Javascript ================ Switchable Fields ----------------- By marking fields with the ``"switchable"`` and ``"switched"`` classes along with defining a few data attributes you can programmatically hide, show, and rename fields in a form. The triggers are fields using a ``select`` input widget, marked with the "switchable" class, and defining a "data-slug" attribute. When they are changed, any input with the ``"switched"`` class and defining a ``"data-switch-on"`` attribute which matches the ``select`` input's ``"data-slug"`` attribute will be evaluated for necessary changes. In simpler terms, if the ``"switched"`` target input's ``"switch-on"`` matches the ``"slug"`` of the ``"switchable"`` trigger input, it gets switched. Simple, right? The ``"switched"`` inputs also need to define states. For each state in which the input should be shown, it should define a data attribute like the following: ``data--=""``. When the switch event happens the value of the ``"switchable"`` field will be compared to the data attributes and the correct label will be applied to the field. If a corresponding label for that value is *not* found, the field will be hidden instead. A simplified example is as follows:: source = forms.ChoiceField( label=_('Source'), choices=[ ('cidr', _('CIDR')), ('sg', _('Security Group')) ], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'source' }) ) cidr = fields.IPField( label=_("CIDR"), required=False, widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-cidr': _('CIDR') }) ) security_group = forms.ChoiceField( label=_('Security Group'), required=False, widget=forms.Select(attrs={ 'class': 'switched', 'data-switch-on': 'source', 'data-source-sg': _('Security Group') }) ) That code would create the ``"switchable"`` control field ``source``, and the two ``"switched"`` fields ``cidr`` and ``security group`` which are hidden or shown depending on the value of ``source``. NOTE: A field can only safely define one slug in its ``"switch-on"`` attribute. While switching on multiple fields is possible, the behavior is very hard to predict due to the events being fired from the various switchable fields in order. You generally end up just having it hidden most of the time by accident, so it's not recommended. Instead just add a second field to the form and control the two independently, then merge their results in the form's clean or handle methods at the end. horizon-9.0.0/doc/source/ref/tabs.rst0000664000567000056710000000172212701407063020657 0ustar jenkinsjenkins00000000000000========================== Horizon Tabs and TabGroups ========================== .. module:: horizon.tabs Horizon includes a set of reusable components for programmatically building tabbed interfaces with fancy features like dynamic AJAX loading and nearly effortless templating and styling. Tab Groups ========== For any tabbed interface, your fundamental element is the tab group which contains all your tabs. This class provides a dead-simple API for building tab groups and encapsulates all the necessary logic behind the scenes. .. autoclass:: TabGroup :members: Tabs ==== The tab itself is the discrete unit for a tab group, representing one view of data. .. autoclass:: Tab :members: .. autoclass:: TableTab :members: TabView ======= There is also a useful and simple generic class-based view for handling the display of a :class:`~horizon.tabs.TabGroup` class. .. autoclass:: TabView :members: .. autoclass:: TabbedTableView :members: horizon-9.0.0/doc/source/ref/exceptions.rst0000664000567000056710000000015212701407063022103 0ustar jenkinsjenkins00000000000000================== Horizon Exceptions ================== .. automodule:: horizon.exceptions :members: horizon-9.0.0/doc/source/ref/run_tests.rst0000664000567000056710000002145112701407063021755 0ustar jenkinsjenkins00000000000000=========================== The ``run_tests.sh`` Script =========================== .. contents:: Contents: :local: Horizon ships with a script called ``run_tests.sh`` at the root of the repository. This script provides many crucial functions for the project, and also makes several otherwise complex tasks trivial for you as a developer. First Run ========= If you start with a clean copy of the Horizon repository, the first thing you should do is to run ``./run_tests.sh`` from the root of the repository. This will do two things for you: #. Set up a virtual environment for both the ``horizon`` module and the ``openstack_dashboard`` project using ``./tools/install_venv.py``. #. Run the tests for both ``horizon`` and ``openstack_dashboard`` using their respective environments and verify that everything is working. Setting up the environment the first time can take several minutes, but only needs to be done once. If dependencies are added in the future, updating the environments will be necessary but not as time consuming. I just want to run the tests! ============================= Running the full set of unit tests quickly and easily is the main goal of this script. All you need to do is:: ./run_tests.sh Yep, that's it. However, for a more thorough test run you can include the Selenium tests by using the ``--with-selenium`` flag:: ./run_tests.sh --with-selenium If you run horizon in a minimal installation VM, you will probably need the following (steps for Fedora 18 minimal installation): #. Install these packages in the VM: ``yum install xorg-x11-xauth xorg-x11-fonts-Type1.noarch``. #. Install firefox in the VM: ``yum install firefox``. #. Connect to the VM by ``ssh -X`` (if you run ``set|grep DISP``, you should see that the DISPLAY is set). #. Run ``./run_tests.sh --with-selenium``. Running a subset of tests ------------------------- Instead of running all tests, you can specify an individual directory, file, class, or method that contains test code. To run the tests in the ``horizon/test/tests/tables.py`` file:: ./run_tests.sh horizon.test.tests.tables To run the tests in the `WorkflowsTests` class in ``horizon/test/tests/workflows``:: ./run_tests.sh horizon.test.tests.workflows:WorkflowsTests To run just the `WorkflowsTests.test_workflow_view` test method:: ./run_tests.sh horizon.test.tests.workflows:WorkflowsTests.test_workflow_view Running the integration tests ----------------------------- The Horizon integration tests treat Horizon as a black box, and similar to Tempest must be run against an existing OpenStack system. These tests are not run by default. #. Update the configuration file `openstack_dashboard/test/integration_tests/horizon.conf` as required (the format is similar to the Tempest configuration file). #. Run the tests with the following command: :: $ ./run_tests.sh --integration Like for the unit tests, you can choose to only run a subset. :: $ ./run_tests.sh --integration openstack_dashboard.test.integration_tests.tests.test_login Using Dashboard and Panel Templates =================================== Horizon has a set of convenient management commands for creating new dashboards and panels based on basic templates. Dashboards ---------- To create a new dashboard, run the following:: ./run_tests.sh -m startdash This will create a directory with the given dashboard name, a ``dashboard.py`` module with the basic dashboard code filled in, and various other common "boilerplate" code. Available options: * ``--target``: the directory in which the dashboard files should be created. Default: A new directory within the current directory. Panels ------ To create a new panel, run the following:: ./run_tests -m startpanel This will create a directory with the given panel name, and ``panel.py`` module with the basic panel code filled in, and various other common "boilerplate" code. Available options: * ``-d``, ``--dashboard``: The dotted python path to your dashboard app (the module which containers the ``dashboard.py`` file.). If not specified, the target dashboard should be specified in a pluggable settings file for the panel. * ``--target``: the directory in which the panel files should be created. If the value is ``auto`` the panel will be created as a new directory inside the dashboard module's directory structure. Default: A new directory within the current directory. JavaScript Tests ---------------- You can also run JavaScript unit tests using Karma. Karma is a test environment that allows for multiple test runners and reporters, including such features as code coverage. Karma allows developer to run tests live, as it can watch source and test files for changes. The default configuration also performs coverage reports, which are saved to ``horizon/coverage-karma/`` and ``openstack_dashboard/coverage-karma/``. To run the Karma tests for Horizon and Dashboard using the `run_tests.sh` script:: ./run_tests.sh --karma To run the Karma tests for Horizon and Dashboard using `npm`:: npm install # You only need to execute this once. npm test .. note:: These two methods are equivalent. The former merely executes the latter. JavaScript Code Style Checks ---------------------------- You can run the JavaScript code style checks, or linting, using eslint. ESLint is a permissively licensed, sophisticated language parser and linter that confirms both our style guidelines, and checks the code for common errors that may create unexpected behavior. To run eslint for Horizon and Dashboard using the `run_tests.sh` script:: ./run_tests.sh --eslint To run eslint for Horizon and Dashboard using `npm`:: npm install # You only need to execute this once. npm run lint .. note:: These two methods are equivalent. The former merely executes the latter. Give me metrics! ================ You can generate various reports and metrics using command line arguments to ``run_tests.sh``. ESLint ------ To run ESLint, a JavaScript code style checker:: ./run_tests.sh --eslint Coverage -------- To run coverage reports:: ./run_tests.sh --coverage The reports are saved to ``./reports/`` and ``./coverage.xml``. PEP8 ---- You can check for PEP8 violations as well:: ./run_tests.sh --pep8 The results are saved to ``./pep8.txt``. PyLint ------ For more detailed code analysis you can run:: ./run_tests.sh --pylint The output will be saved in ``./pylint.txt``. Tab Characters -------------- For those who dislike having a mix of tab characters and spaces for indentation there's a command to check for that in Python, CSS, JavaScript and HTML files:: ./run_tests.sh --tabs This will output a total "tab count" and a list of the offending files. Running the development server ============================== As an added bonus, you can run Django's development server directly from the root of the repository with ``run_tests.sh`` like so:: ./run_tests.sh --runserver This is effectively just an alias for:: ./tools/with_venv.sh ./manage.py runserver Generating the documentation ============================ You can build Horizon's documentation automatically by running:: ./run_tests.sh --docs The output is stored in ``./doc/build/html/``. Updating the translation files ============================== You can update all of the translation files for both the ``horizon`` app and ``openstack_dashboard`` project with a single command:: ./run_tests.sh --makemessages or, more compactly:: ./run_tests.sh --m Starting clean ============== If you ever want to start clean with a new environment for Horizon, you can run:: ./run_tests.sh --force That will blow away the existing environments and create new ones for you. Non-interactive Mode ==================== There is an optional flag which will run the script in a non-interactive (and eventually less verbose) mode:: ./run_tests.sh --quiet This will automatically take the default action for actions which would normally prompt for user input such as installing/updating the environment. Environment Backups =================== To speed up the process of doing clean checkouts, running continuous integration tests, etc. there are options for backing up the current environment and restoring from a backup:: ./run_tests.sh --restore-environment ./run_tests.sh --backup-environment The environment backup is stored in ``/tmp/.horizon_environment/``. Environment Versioning ====================== Horizon keeps track of changes to the environment by comparing the current requirements files (``requirements.txt`` and ``test-requirements.txt``) and the files last time the virtual environment was created or updated. If there is any difference, the virtual environment will be update automatically when you run ``run_tests.sh``. horizon-9.0.0/doc/source/ref/tables.rst0000664000567000056710000000425012701407063021177 0ustar jenkinsjenkins00000000000000================== Horizon DataTables ================== .. module:: horizon.tables Horizon includes a componentized API for programmatically creating tables in the UI. Why would you want this? It means that every table renders correctly and consistently, table- and row-level actions all have a consistent API and appearance, and generally you don't have to reinvent the wheel or copy-and-paste every time you need a new table! .. seealso:: For usage information, tips & tricks and more examples check out the :doc:`DataTables Topic Guide `. DataTable ========= The core class which defines the high-level structure of the table being represented. Example:: class MyTable(DataTable): name = Column('name') email = Column('email') class Meta: name = "my_table" table_actions = (MyAction, MyOtherAction) row_actions - (MyAction) A full reference is included below: .. autoclass:: DataTable :members: DataTable Options ================= The following options can be defined in a ``Meta`` class inside a :class:`.DataTable` class. Example:: class MyTable(DataTable): class Meta: name = "my_table" verbose_name = "My Table" .. autoclass:: horizon.tables.base.DataTableOptions :members: FormsetDataTable ================ You can integrate the :class:`.DataTable` with a Django Formset using one of following classes: .. autoclass:: horizon.tables.formset.FormsetDataTableMixin :members: .. autoclass:: horizon.tables.formset.FormsetDataTable :members: Table Components ================ .. autoclass:: Column :members: .. autoclass:: Row :members: Actions ======= .. autoclass:: Action :members: .. autoclass:: LinkAction :members: .. autoclass:: FilterAction :members: .. autoclass:: FixedFilterAction :members: .. autoclass:: BatchAction :members: .. autoclass:: DeleteAction :members: .. autoclass:: UpdateAction :members: Class-Based Views ================= Several class-based views are provided to make working with DataTables easier in your UI. .. autoclass:: DataTableView .. autoclass:: MultiTableView horizon-9.0.0/doc/source/ref/middleware.rst0000664000567000056710000000015312701407063022040 0ustar jenkinsjenkins00000000000000================== Horizon Middleware ================== .. automodule:: horizon.middleware :members: horizon-9.0.0/doc/source/ref/local_conf.rst0000664000567000056710000000531112701407063022023 0ustar jenkinsjenkins00000000000000========== local.conf ========== Configuring DevStack for Horizon ================================ Place the following content into `devstack/local.conf` to start the services that Horizon supports in DevStack when `stack.sh` is run. :: [[local|localrc]] ADMIN_PASSWORD=secretadmin MYSQL_PASSWORD=secretadmin RABBIT_PASSWORD=secretadmin SERVICE_PASSWORD=secretadmin SERVICE_TOKEN=a682f596-76f3-11e3-b3b2-e716f9080d50 # Recloning will insure that your stack is up to date. The downside # is overhead on restarts and potentially losing a stable environment. # If set to yes, will reclone all repos every time stack.sh is run. # The default is no. #RECLONE=yes # Set ``OFFLINE`` to ``True`` to configure ``stack.sh`` to run cleanly without # Internet access. ``stack.sh`` must have been previously run with Internet # access to install prerequisites and fetch repositories. # OFFLINE=True # Note: there are several network setting changes that may be # required to get networking properly configured in your environment. # This file is just using the defaults set up by devstack. # For a more detailed treatment of devstack network configuration # options, please see: http://devstack.org/guides/single-machine.html ### SERVICES # Enable Swift (Object Store) without replication enable_service s-proxy s-object s-container s-account SWIFT_HASH=66a3d6b56c1f479c8b4e70ab5c2000f5 SWIFT_REPLICAS=1 SWIFT_DATA_DIR=$DEST/data/swift # Enable Neutron (Networking) # to use nova net rather than neutron, comment out the following group disable_service n-net enable_plugin neutron https://git.openstack.org/openstack/neutron enable_service q-svc enable_service q-agt enable_service q-dhcp enable_service q-l3 enable_service q-meta enable_service q-metering enable_service q-qos # end group # Enable VPN plugin for neutron enable_plugin neutron-vpnaas https://git.openstack.org/openstack/neutron-vpnaas # Enable Firewall plugin for neutron enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas # Enable Load Balancer plugin for neutron enable_plugin neutron-lbaas https://git.openstack.org/openstack/neutron-lbaas # Enable Ceilometer (Metering) enable_service ceilometer-acompute ceilometer-acentral ceilometer-anotification ceilometer-collector ceilometer-api ### PLUGINS # Enable Sahara (Data Processing) enable_plugin sahara git://git.openstack.org/openstack/sahara # Enable Trove (Database) enable_plugin trove git://git.openstack.org/openstack/trove [[post-config|$GLANCE_API_CONF]] [DEFAULT] default_store=file horizon-9.0.0/doc/source/ref/decorators.rst0000664000567000056710000000015212701407063022067 0ustar jenkinsjenkins00000000000000================== Horizon Decorators ================== .. automodule:: horizon.decorators :members: horizon-9.0.0/doc/source/ref/workflows.rst0000664000567000056710000000123112701407063021756 0ustar jenkinsjenkins00000000000000================= Horizon Workflows ================= .. module:: horizon.workflows One of the most challenging aspects of building a compelling user experience is crafting complex multi-part workflows. Horizon's ``workflows`` module aims to bring that capability within everyday reach. .. seealso:: For usage information, tips & tricks and more examples check out the :doc:`Workflows Topic Guide `. Workflows ========= .. autoclass:: Workflow :members: Steps ===== .. autoclass:: Step :members: Actions ======= .. autoclass:: Action :members: WorkflowView ============ .. autoclass:: WorkflowView :members: horizon-9.0.0/doc/source/ref/horizon.rst0000664000567000056710000000151712701407063021420 0ustar jenkinsjenkins00000000000000====================== The ``horizon`` Module ====================== .. module:: horizon Horizon ships with a single point of contact for hooking into your project if you aren't developing your own :class:`~horizon.Dashboard` or :class:`~horizon.Panel`:: import horizon From there you can access all the key methods you need. Horizon ======= .. attribute:: urls The auto-generated URLconf for horizon. Usage:: url(r'', include(horizon.urls)), .. autofunction:: register .. autofunction:: unregister .. autofunction:: get_absolute_url .. autofunction:: get_user_home .. autofunction:: get_dashboard .. autofunction:: get_default_dashboard .. autofunction:: get_dashboards Dashboard ========= .. autoclass:: Dashboard :members: Panel ===== .. autoclass:: Panel :members: .. autoclass:: PanelGroup :members: horizon-9.0.0/doc/source/ref/test.rst0000664000567000056710000000111612701407063020702 0ustar jenkinsjenkins00000000000000======================== Horizon TestCase Classes ======================== .. module:: horizon.test.helpers Horizon provides a base test case class which provides several useful pre-prepared attributes for testing Horizon components. .. autoclass:: TestCase :members: .. module :: openstack_dashboard.test.helpers The OpenStack Dashboard also provides test case classes for greater ease-of-use when testing APIs and OpenStack-specific auth scenarios. .. autoclass:: TestCase :members: .. autoclass:: APITestCase :members: .. autoclass:: BaseAdminViewTests :members: horizon-9.0.0/doc/source/glossary.rst0000664000567000056710000000113012701407063021006 0ustar jenkinsjenkins00000000000000======== Glossary ======== Horizon The OpenStack dashboard project. Also the name of the top-level Python object which handles registration for the app. Dashboard A Python class representing a top-level navigation item (e.g. "project") which provides a consistent API for Horizon-compatible applications. Panel A Python class representing a sub-navigation item (e.g. "instances") which contains all the necessary logic (views, forms, tests, etc.) for that interface. Project Used in user-facing text in place of the term "Tenant" which is Keystone's word. horizon-9.0.0/doc/source/topics/0000775000567000056710000000000012701407231017714 5ustar jenkinsjenkins00000000000000horizon-9.0.0/doc/source/topics/settings.rst0000664000567000056710000016045312701407071022321 0ustar jenkinsjenkins00000000000000========================== Settings and Configuration ========================== Introduction ============ Horizon's settings tend to fall into three categories: * Horizon configuration options (contained in the ``HORIZON_CONFIG`` dict) which are not OpenStack-specific and pertain only to the core framework. * OpenStack-related settings which pertain to other projects/services and are generally prefixed with ``OPENSTACK_`` in the settings file. * Django settings (including common plugins like ``django-compressor``) which can be (and should be) read about in their respective documentation. What follows is an overview of the Horizon and OpenStack-specific settings and a few notes on the Django-related settings. .. note:: Prior to the Essex release of Horizon there were settings which controlled whether features such as Object Storage/Swift or Networking/Neutron would be enabled in the OpenStack Dashboard. This code has long since been removed and those pre-Essex settings have no impact now. In Essex and later, the Service Catalog returned by the Identity Service after a user has successfully authenticated determines the dashboards and panels that will be available within the OpenStack Dashboard. If you are not seeing a particular service you expected make sure your Service Catalog is configured correctly. Horizon Settings ================ The following options are available in order to configure/customize the behavior of your Horizon installation. All of them are contained in the ``HORIZON_CONFIG`` dictionary. .. _dashboards: ``dashboards`` -------------- .. warning:: In OpenStack Dashboard configuration, we suggest **NOT** to use this setting. Please specify the order of dashboard using the :ref:`pluggable-settings-label`. Both the pluggable dashboard mechanism (OpenStack Dashboard default) and this setting ``dashboard`` configure the order of dashboards and the setting ``dashboard`` precedes the pluggable dashboard mechanism. Specifying the order in two places may cause confusion. Please use this parameter only when the pluggable config is not used. .. versionadded:: 2012.1(Essex) Default: ``None`` Horizon Dashboards are automatically discovered in the following way: * By adding a configuration file to the ``openstack_dashboard/local/enabled`` directory (for more information see :ref:`pluggable-settings-label`). This is the default way in OpenStack Dashboard. * By traversing Django's list of `INSTALLED_APPS `_ and importing any files that have the name ``"dashboard.py"`` and include code to register themselves as a Horizon dashboard. By default, dashboards defined by ``openstack_dashboard/local/enabled`` are displayed first in the alphabetical order of the config files, and then the remaining dashboards discovered by traversing INSTALLED_APPS are displayed in the alphabetical order. If a list of ``dashboard`` slugs is provided in this setting, the supplied ordering is applied to the list of discovered dashboards. If the list of dashboard slugs is shorter than the number of discovered dashboards, the remaining dashboards are appended in the default order described above. The dashboards listed must be in a Python module which is included in the ``INSTALLED_APPS`` list and on the Python path. ``default_dashboard`` --------------------- .. warning:: In OpenStack Dashboard configuration, we suggest **NOT** to use this setting. Please specify the order of dashboard using the :ref:`pluggable-settings-label`. The default dashboard can be configured via both the pluggable dashboard mechanism (OpenStack Dashboard default) and this setting ``default_dashboard``, and if both are specified, the setting by the pluggable dashboard mechanism will be used. Specifying the default dashboard in two places may cause confusion. Please use this parameter only when the pluggable config is not used. .. versionadded:: 2012.1(Essex) Default: ``None`` The slug of the dashboard which should act as the first-run/fallback dashboard whenever a user logs in or is otherwise redirected to an ambiguous location. ``user_home`` ------------- .. versionadded:: 2012.1(Essex) Default: ``settings.LOGIN_REDIRECT_URL`` This can be either a literal URL path (such as the default), or Python's dotted string notation representing a function which will evaluate what URL a user should be redirected to based on the attributes of that user. ``ajax_queue_limit`` -------------------- .. versionadded:: 2012.1(Essex) Default: ``10`` The maximum number of simultaneous AJAX connections the dashboard may try to make. This is particularly relevant when monitoring a large number of instances, volumes, etc. which are all actively trying to update/change state. ``ajax_poll_interval`` ---------------------- .. versionadded:: 2012.1(Essex) Default: ``2500`` How frequently resources in transition states should be polled for updates, expressed in milliseconds. ``auto_fade_alerts`` -------------------- .. versionadded:: 2013.2(Havana) Defaults: ``{'delay': [3000], 'fade_duration': [1500], 'types': []}`` If provided, will auto-fade the alert types specified. Valid alert types include: ['alert-default', 'alert-success', 'alert-info', 'alert-warning', 'alert-danger'] Can also define the delay before the alert fades and the fade out duration. ``bug_url`` ----------- .. versionadded:: 9.0.0(Mitaka) Default: ``None`` If provided, a "Report Bug" link will be displayed in the site header which links to the value of this setting (ideally a URL containing information on how to report issues). ``help_url`` ------------ .. versionadded:: 2012.2(Folsom) Default: ``None`` If provided, a "Help" link will be displayed in the site header which links to the value of this setting (ideally a URL containing help information). ``exceptions`` -------------- .. versionadded:: 2012.1(Essex) Default: ``{'unauthorized': [], 'not_found': [], 'recoverable': []}`` A dictionary containing classes of exceptions which Horizon's centralized exception handling should be aware of. Based on these exception categories, Horizon will handle the exception and display a message to the user. ``modal_backdrop`` ------------------ .. versionadded:: 2014.2(Kilo) Default: ``"static"`` Controls how bootstrap backdrop element outside of modals looks and feels. Valid values are ``"true"`` (show backdrop element outside the modal, close the modal after clicking on backdrop), ``"false"`` (do not show backdrop element, do not close the modal after clicking outside of it) and ``"static"`` (show backdrop element outside the modal, do not close the modal after clicking on backdrop). ``disable_password_reveal`` --------------------------- .. versionadded:: 2015.1(Kilo) Default: ``False`` Setting this to True will disable the reveal button for password fields, including on the login form. ``password_validator`` ---------------------- .. versionadded:: 2012.1(Essex) Default: ``{'regex': '.*', 'help_text': _("Password is not accepted")}`` A dictionary containing a regular expression which will be used for password validation and help text which will be displayed if the password does not pass validation. The help text should describe the password requirements if there are any. This setting allows you to set rules for passwords if your organization requires them. ``password_autocomplete`` ------------------------- .. versionadded:: 2013.1(Grizzly) Default: ``"off"`` Controls whether browser autocompletion should be enabled on the login form. Valid values are ``"on"`` and ``"off"``. ``simple_ip_management`` ------------------------ .. versionadded:: 2013.1(Grizzly) Default: ``True`` Enable or disable simplified floating IP address management. "Simple" floating IP address management means that the user does not ever have to select the specific IP addresses they wish to use, and the process of allocating an IP and assigning it to an instance is one-click. The "advanced" floating IP management allows users to select the floating IP pool from which the IP should be allocated and to select a specific IP address when associating one with an instance. .. note:: Currently "simple" floating IP address management is not compatible with Neutron. There are two reasons for this. First, Neutron does not support the default floating IP pool at the moment. Second, a Neutron floating IP can be associated with each VIF and we need to check whether there is only one VIF for an instance to enable simple association support. ``angular_modules`` ------------------------- Default: ``[]`` A list of AngularJS modules to be loaded when Angular bootstraps. These modules are added as dependencies on the root Horizon application ``horizon``. ``js_files`` ------------------------- Default: ``[]`` A list of javascript source files to be included in the compressed set of files that are loaded on every page. This is needed for AngularJS modules that are referenced in ``angular_modules`` and therefore need to be include in every page. ``js_spec_files`` ------------------------- .. versionadded:: 2015.1(Kilo) Default: ``[]`` A list of javascript spec files to include for integration with the Jasmine spec runner. Jasmine is a behavior-driven development framework for testing JavaScript code. OpenStack Settings (Partial) ============================ The following settings inform the OpenStack Dashboard of information about the other OpenStack projects which are part of this cloud and control the behavior of specific dashboards, panels, API calls, etc. Most of the following settings are defined in ``openstack_dashboard/local/local_settings.py``, which should be copied from ``openstack_dashboard/local/local_settings.py.example``. Since Mitaka, there is also a way to drop file snippets into ``openstack_dashboard/local/local_settings.d/``. These snippets must end with ``.py`` and must contain valid Python code. The snippets are loaded after ``local_settings.py`` is evaluated so you are able to override settings from ``local_settings.py`` without the need to change this file. Snippets are evaluated in alphabetical order by file name. It's good style to name the files in ``local_settings.d/`` like ``_ZZ_another_setting.py`` where ``ZZ`` is a number. The file must start with an underscore (``_``) because Python can not load files starting with a number. So given that you have 3 files, ``local_settings.py``, ``local_settings.d/_10_setting_one.py`` and ``local_settings.d/_20_settings_two.py``, the settings from ``local_settings.py`` are evaluated first. Settings from ``local_settings.d/_10_settings_one.py`` override settings from ``local_settings.py`` and settings from ``local_settings.d/_20_settings_two.py`` override all other settings because that's the file which is evaluated last. ``AUTHENTICATION_URLS`` ----------------------- .. versionadded:: 2015.1(Kilo) Default: ``['openstack_auth.urls']`` A list of modules from which to collate authentication URLs from. The default option adds URLs from the django-openstack-auth module however others will be required for additional authentication mechanisms. ``API_RESULT_LIMIT`` -------------------- .. versionadded:: 2012.1(Essex) Default: ``1000`` The maximum number of objects (e.g. Swift objects or Glance images) to display on a single page before providing a paging element (a "more" link) to paginate results. ``API_RESULT_PAGE_SIZE`` ------------------------ .. versionadded:: 2012.2(Folsom) Default: ``20`` Similar to ``API_RESULT_LIMIT``. This setting controls the number of items to be shown per page if API pagination support for this exists. ``AVAILABLE_REGIONS`` --------------------- .. versionadded:: 2012.1(Essex) Default: ``None`` A list of tuples which define multiple regions. The tuple format is ``('http://{{ keystone_host }}:5000/v2.0', '{{ region_name }}')``. If any regions are specified the login form will have a dropdown selector for authenticating to the appropriate region, and there will be a region switcher dropdown in the site header when logged in. If you do not have multiple regions you should use the ``OPENSTACK_HOST`` and ``OPENSTACK_KEYSTONE_URL`` settings instead. ``CONSOLE_TYPE`` ---------------- .. versionadded:: 2013.2(Havana) Default: ``"AUTO"`` This setting specifies the type of in-browser console used to access the VMs. Valid values are ``"AUTO"``(default), ``"VNC"``, ``"SPICE"``, ``"RDP"``, ``"SERIAL"``, and ``None``. ``None`` deactivates the in-browser console and is available in version 2014.2(Juno). ``"SERIAL"`` is available since 2015.1(Kilo). ``SWIFT_FILE_TRANSFER_CHUNK_SIZE`` ---------------------------------- .. versionadded:: 2015.1(Kilo) Default: ``512 * 1024`` This setting specifies the size of the chunk (in bytes) for downloading objects from Swift. Do not make it very large (higher than several dozens of Megabytes, exact number depends on your connection speed), otherwise you may encounter socket timeout. The default value is 524288 bytes (or 512 Kilobytes). ``INSTANCE_LOG_LENGTH`` ----------------------- .. versionadded:: 2015.1(Kilo) Default: ``35`` This setting enables you to change the default number of lines displayed for the log of an instance. Valid value must be a positive integer. ``CREATE_INSTANCE_FLAVOR_SORT`` ------------------------------- .. versionadded:: 2013.2(Havana) Default: ``{'key':'ram'}`` When launching a new instance the default flavor is sorted by RAM usage in ascending order. You can customize the sort order by: id, name, ram, disk and vcpus. Additionally, you can insert any custom callback function. You can also provide a flag for reverse sort. See the description in local_settings.py.example for more information. This example sorts flavors by vcpus in descending order:: CREATE_INSTANCE_FLAVOR_SORT = { 'key':'vcpus', 'reverse': True, } .. _available_themes: ``AVAILABLE_THEMES`` -------------------- .. versionadded:: 9.0.0(Mitaka) Default: ``AVAILABLE_THEMES = [ ('default', 'Default', 'themes/default'), ('material', 'Material', 'themes/material'), ]`` This setting tells Horizon which themes to use. A list of tuples which define multiple themes. The tuple format is ``('{{ theme_name }}', '{{ theme_label }}', '{{ theme_path }}')``. The ``theme_name`` is the name used to define the directory which the theme is collected into, under ``/{{ THEME_COLLECTION_DIR }}``. It also specifies the key by which the selected theme is stored in the browser's cookie. The ``theme_label`` is the user-facing label that is shown in the theme picker. The theme picker is only visible if more than one theme is configured, and shows under the topnav's user menu. By default, the ``theme path`` is the directory that will serve as the static root of the theme and the entire contents of the directory is served up at ``/{{ THEME_COLLECTION_DIR }}/{{ theme_name }}``. If you wish to include content other than static files in a theme directory, but do not wish that content to be served up, then you can create a sub directory named ``static``. If the theme folder contains a sub-directory with the name ``static``, then ``static/custom/static``` will be used as the root for the content served at ``/static/custom``. The static root of the theme folder must always contain a _variables.scss file and a _styles.scss file. These must contain or import all the bootstrap and horizon specific variables and styles which are used to style the GUI. For example themes, see: /horizon/openstack_dashboard/themes/ Horizon ships with two themes configured. 'default' is the default theme, and 'material' is based on Google's Material Design. ``DEFAULT_THEME`` ----------------- .. versionadded:: 9.0.0(Mitaka) Default: ``"default"`` This setting tells Horizon which theme to use if the user has not yet selected a theme through the theme picker and therefore set the cookie value. This value represents the ``theme_name`` key that is used from ``AVAILABLE_THEMES``. To use this setting, the theme must also be configured inside of ``AVAILABLE_THEMES``. ``THEME_COLLECTION_DIR`` ------------------------ .. versionadded:: 9.0.0(Mitaka) Default: ``"themes"`` This setting tells Horizon which static directory to collect the available themes into, and therefore which URL points to the theme colleciton root. For example, the default theme would be accessible via ``/{{ STATIC_URL }}/themes/default``. ``THEME_COOKIE_NAME`` --------------------- .. versionadded:: 9.0.0(Mitaka) Default: ``"theme"`` This setting tells Horizon in which cookie key to store the currently set theme. The cookie expiration is currently set to a year. .. _custom_theme_path: ``CUSTOM_THEME_PATH`` --------------------- .. versionadded:: 2015.1(Kilo) (Deprecated) Default: ``"themes/default"`` This setting tells Horizon to use a directory as a custom theme. By default, this directory will serve as the static root of the theme and the entire contents of the directory will be served up at ``/static/custom``. If you wish to include content other than static files in a theme directory, but do not wish that content to be served up, then you can create a sub directory named ``static``. If the theme folder contains a sub-directory with the name ``static``, then ``static/custom/static``` will be used as the root for the content served at ``/static/custom``. The static root of the theme folder must always contain a _variables.scss file and a _styles.scss file. These must contain or import all the bootstrap and horizon specific variables and styles which are used to style the GUI. For example themes, see: /horizon/openstack_dashboard/themes/ Horizon ships with one alternate theme based on Google's Material Design. To use the alternate theme, set your CUSTOM_THEME_PATH to ``themes/material``. This option is now marked as "deprecated" and will be removed in Newton or a later release. Themes are now controlled by AVAILABLE_THEMES. We suggest changing your custom theme settings to use this option instead. ``DEFAULT_THEME_PATH`` ---------------------- .. versionadded:: 8.0.0(Liberty) (Deprecated) Default: ``"themes/default"`` This setting allows Horizon to collect an additional theme during static collection and be served up via /static/themes/default. This is useful if CUSTOM_THEME_PATH inherits from another theme (like 'default'). If DEFAULT_THEME_PATH is the same as CUSTOM_THEME_PATH, then collection is skipped and /static/themes will not exist. This option is now marked as "deprecated" and will be removed in Newton or a later release. Themes are now controlled by AVAILABLE_THEMES. ``DROPDOWN_MAX_ITEMS`` ---------------------- .. versionadded:: 2015.1(Kilo) Default: ``30`` This setting sets the maximum number of items displayed in a dropdown. Dropdowns that limit based on this value need to support a way to observe the entire list. ``ENFORCE_PASSWORD_CHECK`` -------------------------- .. versionadded:: 2015.1(Kilo) Default: ``False`` This setting will display an 'Admin Password' field on the Change Password form to verify that it is indeed the admin logged-in who wants to change the password. ``IMAGES_LIST_FILTER_TENANTS`` ------------------------------ .. versionadded:: 2013.1(Grizzly) Default: ``None`` A list of dictionaries to add optional categories to the image fixed filters in the Images panel, based on project ownership. Each dictionary should contain a `tenant` attribute with the project id, and optionally a `text` attribute specifying the category name, and an `icon` attribute that displays an icon in the filter button. The icon names are based on the default icon theme provided by Bootstrap. Example: ``[{'text': 'Official', 'tenant': '27d0058849da47c896d205e2fc25a5e8', 'icon': 'icon-ok'}]`` .. note:: Since the Kilo release, the Bootstrap icon library (e.g. 'icon-ok') has been replaced with Font Awesome (e.g. 'fa-check'). ``IMAGE_RESERVED_CUSTOM_PROPERTIES`` ------------------------------------ .. versionadded:: 2014.2(Juno) Default: ``[]`` A list of image custom property keys that should not be displayed in the Update Metadata tree. This setting can be used in the case where a separate panel is used for managing a custom property or if a certain custom property should never be edited. ``LAUNCH_INSTANCE_DEFAULTS`` ---------------------------- .. versionadded:: 9.0.0(Mitaka) Default:: { "config_drive": False } A dictionary of settings which can be used to provide the default values for properties found in the Launch Instance modal. The ``config_drive`` setting specifies the default value for the Configuration Drive property. ``LAUNCH_INSTANCE_NG_ENABLED`` ------------------------------ .. versionadded:: 8.0.0(Liberty) Default: ``True`` This setting enables the AngularJS Launch Instance workflow. .. note:: The default value for this has been changed to ``True`` in 9.0.0 (Mitaka) .. note:: It is possible to run both the AngularJS and Python workflows simultaneously, so the other may be need to be toggled with ``LAUNCH_INSTANCE_LEGACY_ENABLED`` ``LAUNCH_INSTANCE_LEGACY_ENABLED`` ---------------------------------- .. versionadded:: 8.0.0(Liberty) Default: ``False`` This setting enables the Python Launch Instance workflow. .. note:: The default value for this has been changed to ``False`` in 9.0.0 (Mitaka) .. note:: It is possible to run both the AngularJS and Python workflows simultaneously, so the other may be need to be toggled with ``LAUNCH_INSTANCE_NG_ENABLED`` ``MESSAGES_PATH`` ----------------- .. versionadded:: 9.0.0(Mitaka) Default: ``None`` The absolute path to the directory where message files are collected. When the user logins to horizon, the message files collected are processed and displayed to the user. Each message file should contain a JSON formatted data and must have a .json file extension. For example:: { "level": "info", "message": "message of the day here" } Possible values for level are: success, info, warning and error. ``OPENSTACK_API_VERSIONS`` -------------------------- .. versionadded:: 2013.2(Havana) Default:: { "data-processing": 1.1, "identity": 2.0, "volume": 2, "compute": 2 } Overrides for OpenStack API versions. Use this setting to force the OpenStack dashboard to use a specific API version for a given service API. .. note:: The version should be formatted as it appears in the URL for the service API. For example, the identity service APIs have inconsistent use of the decimal point, so valid options would be "2.0" or "3". For example:: OPENSTACK_API_VERSIONS = { "data-processing": 1.1, "identity": 3, "volume": 2, "compute": 2 } ``OPENSTACK_ENABLE_PASSWORD_RETRIEVE`` -------------------------------------- .. versionadded:: 2014.1(Icehouse) Default: ``"False"`` When set, enables the instance action "Retrieve password" allowing password retrieval from metadata service. ``OPENSTACK_ENDPOINT_TYPE`` --------------------------- .. versionadded:: 2012.1(Essex) Default: ``"publicURL"`` A string which specifies the endpoint type to use for the endpoints in the Keystone service catalog. The default value for all services except for identity is ``"publicURL"`` . The default value for the identity service is ``"internalURL"``. ``OPENSTACK_HOST`` ------------------ .. versionadded:: 2012.1(Essex) Default: ``"127.0.0.1"`` The hostname of the Keystone server used for authentication if you only have one region. This is often the *only* setting that needs to be set for a basic deployment. .. _hypervisor-settings-label: ``OPENSTACK_HYPERVISOR_FEATURES`` --------------------------------- .. versionadded:: 2012.2(Folsom) Default:: { 'can_set_mount_point': False, 'can_set_password': False, 'requires_keypair': False, } A dictionary containing settings which can be used to identify the capabilities of the hypervisor for Nova. The Xen Hypervisor has the ability to set the mount point for volumes attached to instances (other Hypervisors currently do not). Setting ``can_set_mount_point`` to ``True`` will add the option to set the mount point from the UI. Setting ``can_set_password`` to ``True`` will enable the option to set an administrator password when launching or rebuilding an instance. Setting ``requires_keypair`` to ``True`` will require users to select a key pair when launching an instance. ``OPENSTACK_IMAGE_BACKEND`` --------------------------- .. versionadded:: 2013.2(Havana) Default:: { 'image_formats': [ ('', _('Select format')), ('aki', _('AKI - Amazon Kernel Image')), ('ami', _('AMI - Amazon Machine Image')), ('ari', _('ARI - Amazon Ramdisk Image')), ('docker', _('Docker')), ('iso', _('ISO - Optical Disk Image')), ('qcow2', _('QCOW2 - QEMU Emulator')), ('raw', _('Raw')), ('vdi', _('VDI')), ('vhd', _('VHD')), ('vmdk', _('VMDK')) ] } Used to customize features related to the image service, such as the list of supported image formats. ``IMAGE_CUSTOM_PROPERTY_TITLES`` -------------------------------- .. versionadded:: 2014.1(Icehouse) Default:: { "architecture": _("Architecture"), "kernel_id": _("Kernel ID"), "ramdisk_id": _("Ramdisk ID"), "image_state": _("Euca2ools state"), "project_id": _("Project ID"), "image_type": _("Image Type") } Used to customize the titles for image custom property attributes that appear on image detail pages. ``HORIZON_IMAGES_ALLOW_UPLOAD`` -------------------------------- .. versionadded:: 2013.1(Grizzly) Default: ``True`` If set to ``False``, this setting disables *local* uploads to prevent filling up the disk on the dashboard server since uploads to the Glance image store service tend to be particularly large - in the order of hundreds of megabytes to multiple gigabytes. .. note:: This will not disable image creation altogether, as this setting does not affect images created by specifying an image location (URL) as the image source. ``OPENSTACK_KEYSTONE_BACKEND`` ------------------------------ .. versionadded:: 2012.1(Essex) Default: ``{'name': 'native', 'can_edit_user': True, 'can_edit_project': True}`` A dictionary containing settings which can be used to identify the capabilities of the auth backend for Keystone. If Keystone has been configured to use LDAP as the auth backend then set ``can_edit_user`` and ``can_edit_project`` to ``False`` and name to ``"ldap"``. ``OPENSTACK_KEYSTONE_DEFAULT_DOMAIN`` ------------------------------------- .. versionadded:: 2013.2(Havana) Default: ``"Default"`` Overrides the default domain used when running on single-domain model with Keystone V3. All entities will be created in the default domain. ``OPENSTACK_KEYSTONE_DEFAULT_ROLE`` ----------------------------------- .. versionadded:: 2011.3(Diablo) Default: ``"_member_"`` The name of the role which will be assigned to a user when added to a project. This value must correspond to an existing role name in Keystone. In general, the value should match the ``member_role_name`` defined in ``keystone.conf``. ``OPENSTACK_KEYSTONE_ADMIN_ROLES`` ---------------------------------- .. versionadded:: 2015.1(Kilo) Default: ``["admin"]`` The list of roles that have administrator privileges in this OpenStack installation. This check is very basic and essentially only works with keystone v2.0 and v3 with the default policy file. The setting assumes there is a common ``admin`` like role(s) across services. Example uses of this setting are: * to rename the ``admin`` role to ``cloud-admin`` * allowing multiple roles to have administrative privileges, like ``["admin", "cloud-admin", "net-op"]`` ``OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT`` ------------------------------------------ .. versionadded:: 2013.2(Havana) Default: ``False`` Set this to True if running on multi-domain model. When this is enabled, it will require user to enter the Domain name in addition to username for login. ``OPENSTACK_KEYSTONE_URL`` -------------------------- .. versionadded:: 2011.3(Diablo) Default: ``"http://%s:5000/v2.0" % OPENSTACK_HOST`` The full URL for the Keystone endpoint used for authentication. Unless you are using HTTPS, running your Keystone server on a nonstandard port, or using a nonstandard URL scheme you shouldn't need to touch this setting. ``OPENSTACK_KEYSTONE_FEDERATION_MANAGEMENT`` -------------------------------------------- .. versionadded:: 9.0.0(Mitaka) Default: ``False`` Set this to True to enable panels that provide the ability for users to manage Identity Providers (IdPs) and establish a set of rules to map federation protocol attributes to Identity API attributes. This extension requires v3.0+ of the Identity API. ``WEBSSO_ENABLED`` ------------------ .. versionadded:: 2015.1(Kilo) Default: ``False`` Enables keystone web single-sign-on if set to True. For this feature to work, make sure that you are using Keystone V3 and Django OpenStack Auth V1.2.0 or later. ``WEBSSO_INITIAL_CHOICE`` ------------------------- .. versionadded:: 2015.1(Kilo) Default: ``"credentials"`` Determines the default authentication mechanism. When user lands on the login page, this is the first choice they will see. ``WEBSSO_CHOICES`` ------------------ .. versionadded:: 2015.1(Kilo) Default:: ( ("credentials", _("Keystone Credentials")), ("oidc", _("OpenID Connect")), ("saml2", _("Security Assertion Markup Language")) ) This is the list of authentication mechanisms available to the user. It includes Keystone federation protocols such as OpenID Connect and SAML, and also keys that map to specific identity provider and federation protocol combinations (as defined in ``WEBSSO_IDP_MAPPING``). The list of choices is completely configurable, so as long as the id remains intact. Do not remove the credentials mechanism unless you are sure. Once removed, even admins will have no way to log into the system via the dashboard. ``WEBSSO_IDP_MAPPING`` ---------------------- .. versionadded:: 8.0.0(Liberty) Default: ``{}`` A dictionary of specific identity provider and federation protocol combinations. From the selected authentication mechanism, the value will be looked up as keys in the dictionary. If a match is found, it will redirect the user to a identity provider and federation protocol specific WebSSO endpoint in keystone, otherwise it will use the value as the protocol_id when redirecting to the WebSSO by protocol endpoint. Example:: WEBSSO_CHOICES = ( ("credentials", _("Keystone Credentials")), ("oidc", _("OpenID Connect")), ("saml2", _("Security Assertion Markup Language")), ("acme_oidc", "ACME - OpenID Connect"), ("acme_saml2", "ACME - SAML2") ) WEBSSO_IDP_MAPPING = { "acme_oidc": ("acme", "oidc"), "acme_saml2": ("acme", "saml2") } .. note:: The value is expected to be a tuple formatted as: (, ). ``OPENSTACK_CINDER_FEATURES`` ----------------------------- .. versionadded:: 2014.2(Juno) Default: ``{'enable_backup': False}`` A dictionary of settings which can be used to enable optional services provided by cinder. Currently only the backup service is available. ``OPENSTACK_HEAT_STACK`` ----------------------------- .. versionadded:: 9.0.0(Mitaka) Default: ``{'enable_user_pass': True}`` A dictionary of settings to use with heat stacks. Currently, the only setting available is "enable_user_pass", which can be used to disable the password field while launching the stack. Currently HEAT API needs user password to perform all the heat operations because in HEAT API trusts is not enabled by default. So, this setting can be set as "False" in-case HEAT uses trusts by default otherwise it needs to be set as "True". ``OPENSTACK_NEUTRON_NETWORK`` ----------------------------- .. versionadded:: 2013.1(Grizzly) Default:: { 'enable_router': True, 'enable_distributed_router': False, 'enable_ha_router': False, 'enable_lb': True, 'enable_quotas': False, 'enable_firewall': True, 'enable_vpn': True, 'profile_support': None, 'supported_provider_types': ["*"], 'supported_vnic_types': ["*"], 'segmentation_id_range': {}, 'enable_fip_topology_check': True, } A dictionary of settings which can be used to enable optional services provided by Neutron and configure Neutron specific features. The following options are available. ``enable_router`` ~~~~~~~~~~~~~~~~~ .. versionadded:: 2014.2(Juno) Default: ``True`` Enable (True) or disable (False) the panels and menus related to router and Floating IP features. This option only affects when Neutron is enabled. If your Neutron deployment has no support for Layer-3 features, or you do not wish to provide the Layer-3 features through the Dashboard, this should be set to ``False``. ``enable_distributed_router`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2014.2(Juno) Default: ``False`` Enable or disable Neutron distributed virtual router (DVR) feature in the Router panel. For the DVR feature to be enabled, this option needs to be set to True and your Neutron deployment must support DVR. Even when your Neutron plugin (like ML2 plugin) supports DVR feature, DVR feature depends on l3-agent configuration, so deployers should set this option appropriately depending on your deployment. ``enable_ha_router`` ~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2014.2(Juno) Default: ``False`` Enable or disable HA (High Availability) mode in Neutron virtual router in the Router panel. For the HA router mode to be enabled, this option needs to be set to True and your Neutron deployment must support HA router mode. Even when your Neutron plugin (like ML2 plugin) supports HA router mode, the feature depends on l3-agent configuration, so deployers should set this option appropriately depending on your deployment. ``enable_lb`` ~~~~~~~~~~~~~ .. versionadded:: 2013.1(Grizzly) (Deprecated) Default: ``True`` Enables the load balancer panel. The load balancer panel will be enabled when this option is True and your Neutron deployment supports LBaaS. If you want to disable load balancer panel even when your Neutron supports LBaaS, set it to False. This option is now marked as "deprecated" and will be removed in Kilo or later release. The load balancer panel is now enabled only when LBaaS feature is available in Neutron and this option is no longer needed. We suggest not to use this option to disable the load balancer panel from now on. ``enable_quotas`` ~~~~~~~~~~~~~~~~~ Default: ``False`` Enable support for Neutron quotas feature. To make this feature work appropriately, you need to use Neutron plugins with quotas extension support and quota_driver should be DbQuotaDriver (default config). ``enable_firewall`` ~~~~~~~~~~~~~~~~~~~ (Deprecated) Default: ``True`` Enables the firewall panel. firewall panel will be enabled when this option is True and your Neutron deployment supports FWaaS. If you want to disable firewall panel even when your Neutron supports FWaaS, set it to False. This option is now marked as "deprecated" and will be removed in Kilo or later release. The firewall panel is now enabled only when FWaaS feature is available in Neutron and this option is no longer needed. We suggest not to use this option to disable the firewall panel from now on. ``enable_vpn`` ~~~~~~~~~~~~~~ (Deprecated) Default: ``True`` Enables the VPN panel. VPN panel will be enabled when this option is True and your Neutron deployment supports VPNaaS. If you want to disable VPN panel even when your Neutron supports VPNaaS, set it to False. This option is now marked as "deprecated" and will be removed in Kilo or later release. The VPN panel is now enabled only when VPNaaS feature is available in Neutron and this option is no longer needed. We suggest not to use this option to disable the VPN panel from now on. ``profile_support`` ~~~~~~~~~~~~~~~~~~~ Default: ``None`` This option specifies a type of network port profile support. Currently the available value is either ``None`` or ``"cisco"``. ``None`` means to disable port profile support. ``cisco`` can be used with Neutron Cisco plugins. ``supported_provider_types`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2014.2(Juno) Default: ``["*"]`` For use with the provider network extension. Use this to explicitly set which provider network types are supported. Only the network types in this list will be available to choose from when creating a network. Network types include local, flat, vlan, gre, and vxlan. By default all provider network types will be available to choose from. Example: ``['local', 'flat', 'gre']`` ``supported_vnic_types`` ~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2015.1(Kilo) Default ``['*']`` For use with the port binding extension. Use this to explicitly set which VNIC types are supported; only those listed will be shown when creating or editing a port. VNIC types include normal, direct and macvtap. By default all VNIC types will be available to choose from. Example ``['normal', 'direct']`` To disable VNIC type selection, set an empty list or None. ``segmentation_id_range`` ~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2014.2(Juno) Default: ``{}`` For use with the provider network extension. This is a dictionary where each key is a provider network type and each value is a list containing two numbers. The first number is the minimum segmentation ID that is valid. The second number is the maximum segmentation ID. Pertains only to the vlan, gre, and vxlan network types. By default this option is not provided and each minimum and maximum value will be the default for the provider network type. Example: ``{'vlan': [1024, 2048], 'gre': [4094, 65536]}`` ``enable_fip_topology_check`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``True`` The Default Neutron implementation needs a router with a gateway to associate a FIP. So by default a topology check will be performed by horizon to list only VM ports attached to a network which is itself attached to a router with an external gateway. This is to prevent from setting a FIP to a port which will fail with an error. Some Neutron vendors do not require it. Some can even attach a FIP to any port (e.g.: OpenContrail) owned by a tenant. Set to False if you want to be able to associate a FIP to an instance on a subnet with no router if your Neutron backend allows it. .. versionadded:: 8.0.0(Liberty) ``default_ipv4_subnet_pool_label`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 8.0.0(Liberty) Default: ``None`` (Disabled) Neutron can be configured with a default Subnet Pool to be used for IPv4 subnet-allocation. Specify the label you wish to display in the Address pool selector on the create subnet step if you want to use this feature. This option is now marked as "deprecated" and will be removed in Newton or a later release. If there exists a default Subnet Pool it will be automatically detected through the Neutron API and the label will be set to the name of the default Subnet Pool. ``default_ipv6_subnet_pool_label`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 8.0.0(Liberty) Default: ``None`` (Disabled) Neutron can be configured with a default Subnet Pool to be used for IPv6 subnet-allocation. Specify the label you wish to display in the Address pool selector on the create subnet step if you want to use this feature. When using Liberty Neutron you must set this to enable IPv6 Prefix Delegation in a PD-capable environment. This option is now marked as "deprecated" and will be removed in Newton or a later release. If there exists a default Subnet Pool it will be automatically detected through the Neutron API and the label will be set to the name of the default Subnet Pool. ``OPENSTACK_SSL_CACERT`` ------------------------ .. versionadded:: 2013.2(Havana) Default: ``None`` When unset or set to ``None`` the default CA certificate on the system is used for SSL verification. When set with the path to a custom CA certificate file, this overrides use of the default system CA certificate. This custom certificate is used to verify all connections to openstack services when making API calls. ``OPENSTACK_SSL_NO_VERIFY`` --------------------------- .. versionadded:: 2012.2(Folsom) Default: ``False`` Disable SSL certificate checks in the OpenStack clients (useful for self-signed certificates). ``OPENSTACK_TOKEN_HASH_ALGORITHM`` ---------------------------------- .. versionadded:: 2014.2(Juno) Default: ``"md5"`` The hash algorithm to use for authentication tokens. This must match the hash algorithm that the identity (Keystone) server and the auth_token middleware are using. Allowed values are the algorithms supported by Python's hashlib library. ``OPENSTACK_TOKEN_HASH_ENABLED`` -------------------------------- .. versionadded:: 8.0.0(Liberty) Default: ``True`` Hashing tokens from Keystone keeps the Horizon session data smaller, but it doesn't work in some cases when using PKI tokens. Uncomment this value and set it to False if using PKI tokens and there are 401 errors due to token hashing. ``POLICY_FILES`` ---------------- .. versionadded:: 2013.2(Havana) Default: ``{'identity': 'keystone_policy.json', 'compute': 'nova_policy.json'}`` This should essentially be the mapping of the contents of ``POLICY_FILES_PATH`` to service types. When policy.json files are added to ``POLICY_FILES_PATH``, they should be included here too. ``POLICY_FILES_PATH`` --------------------- .. versionadded:: 2013.2(Havana) Default: ``os.path.join(ROOT_PATH, "conf")`` Specifies where service based policy files are located. These are used to define the policy rules actions are verified against. ``SESSION_TIMEOUT`` ------------------- .. versionadded:: 2013.2(Havana) Default: ``"3600"`` This SESSION_TIMEOUT is a method to supercede the token timeout with a shorter horizon session timeout (in seconds). So if your token expires in 60 minutes, a value of 1800 will log users out after 30 minutes. ``SAHARA_AUTO_IP_ALLOCATION_ENABLED`` ------------------------------------- Default: ``False`` This setting notifies the Data Processing (Sahara) system whether or not automatic IP allocation is enabled. You would want to set this to True if you were running Nova Networking with auto_assign_floating_ip = True. ``TROVE_ADD_USER_PERMS`` and ``TROVE_ADD_DATABASE_PERMS`` --------------------------------------------------------- .. versionadded:: 2013.2(Havana) Default: ``[]`` Trove user and database extension support. By default, support for creating users and databases on database instances is turned on. To disable these extensions set the permission to something unusable such as ``[!]``. ``WEBROOT`` ----------- .. versionadded:: 2015.1(Kilo) Default: ``"/"`` Specifies the location where the access to the dashboard is configured in the web server. For example, if you're accessing the Dashboard via https:///dashboard, you would set this to ``"/dashboard/"``. .. note:: Additional settings may be required in the config files of your webserver of choice. For example to make ``"/dashboard/"`` the web root in Apache, the ``"sites-available/horizon.conf"`` requires a couple of additional aliases set:: Alias /dashboard/static %HORIZON_DIR%/static Alias /dashboard/media %HORIZON_DIR%/openstack_dashboard/static Apache also requires changing your WSGIScriptAlias to reflect the desired path. For example, you'd replace ``/`` with ``/dashboard`` for the alias. ``STATIC_ROOT`` --------------- .. versionadded:: 8.0.0(Liberty) Default: ``/static`` The absolute path to the directory where static files are collected when collectstatic is run. For more information see: https://docs.djangoproject.com/en/1.7/ref/settings/#static-root ``STATIC_URL`` -------------- .. versionadded:: 8.0.0(Liberty) Default: ``/static/`` URL that refers to files in STATIC_ROOT. By default this value is ``WEBROOT/static/``. This value can be changed from the default. When changed, the alias in your webserver configuration should be updated to match. .. note:: The value for STATIC_URL must end in '/'. This value is also available in the scss namespace with the variable name $static_url. Make sure you run ``python manage.py collectstatic`` and ``python manage.py compress`` after any changes to this value in settings.py. For more information see: https://docs.djangoproject.com/en/1.7/ref/settings/#static-url ``DISALLOW_IFRAME_EMBED`` ------------------------- .. versionadded:: 8.0.0(Liberty) Default: ``True`` This setting can be used to defend against Clickjacking and prevent Horizon from being embedded within an iframe. Legacy browsers are still vulnerable to a Cross-Frame Scripting (XFS) vulnerability, so this option allows extra security hardening where iframes are not used in deployment. When set to true, a ``"frame-buster"`` script is inserted into the template header that prevents the web page from being framed and therefore defends against clickjacking. For more information see: http://tinyurl.com/anticlickjack .. note:: If your deployment requires the use of iframes, you can set this setting to ``False`` to exclude the frame-busting code and allow iframe embedding. ``OPENSTACK_NOVA_EXTENSIONS_BLACKLIST`` --------------------------------------- .. versionadded:: 8.0.0(Liberty) Default: ``[]`` Ignore all listed Nova extensions, and behave as if they were unsupported. Can be used to selectively disable certain costly extensions for performance reasons. Django Settings (Partial) ========================= .. warning:: This is not meant to be anywhere near a complete list of settings for Django. You should always consult the upstream documentation, especially with regards to deployment considerations and security best-practices. There are a few key settings you should be aware of for development and the most basic of deployments. Further recommendations can be found in the Deploying Horizon section of this documentation. ``ALLOWED_HOSTS`` ----------------- .. versionadded:: 2013.2(Havana) Default: ``['localhost']`` This list should contain names (or IP addresses) of the host running the dashboard; if it's being accessed via name, the DNS name (and probably short-name) should be added, if it's accessed via IP address, that should be added. The setting may contain more than one entry. .. note:: ALLOWED_HOSTS is required. If Horizon is running in production (DEBUG is False), set this with the list of host/domain names that the application can serve. For more information see: https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts .. _debug_setting: ``DEBUG`` and ``TEMPLATE_DEBUG`` -------------------------------- .. versionadded:: 2011.2(Cactus) Default: ``True`` Controls whether unhandled exceptions should generate a generic 500 response or present the user with a pretty-formatted debug information page. This setting should **always** be set to ``False`` for production deployments as the debug page can display sensitive information to users and attackers alike. ``SECRET_KEY`` -------------- .. versionadded:: 2012.1(Essex) This should absolutely be set to a unique (and secret) value for your deployment. Unless you are running a load-balancer with multiple Horizon installations behind it, each Horizon instance should have a unique secret key. .. note:: Setting a custom secret key: You can either set it to a specific value or you can let Horizon generate a default secret key that is unique on this machine, regardless of the amount of Python WSGI workers (if used behind Apache+mod_wsgi). However, there may be situations where you would want to set this explicitly, e.g. when multiple dashboard instances are distributed on different machines (usually behind a load-balancer). Either you have to make sure that a session gets all requests routed to the same dashboard instance or you set the same SECRET_KEY for all of them. From horizon.utils import secret_key:: SECRET_KEY = secret_key.generate_or_read_from_file( os.path.join(LOCAL_PATH, '.secret_key_store')) The ``local_settings.py.example`` file includes a quick-and-easy way to generate a secret key for a single installation. ``SECURE_PROXY_SSL_HEADER``, ``CSRF_COOKIE_SECURE`` and ``SESSION_COOKIE_SECURE`` --------------------------------------------------------------------------------- .. versionadded:: 2013.1(Grizzly) These three settings should be configured if you are deploying Horizon with SSL. The values indicated in the default ``local_settings.py.example`` file are generally safe to use. When CSRF_COOKIE_SECURE or SESSION_COOKIE_SECURE are set to True, these attributes help protect the session cookies from cross-site scripting. ``ADD_INSTALLED_APPS`` ---------------------- .. versionadded:: 2015.1(Kilo) A list of Django applications to be prepended to the ``INSTALLED_APPS`` setting. Allows extending the list of installed applications without having to override it completely. .. _pluggable-settings-label: Pluggable Settings ================================= Horizon allows dashboards, panels and panel groups to be added without modifying the default settings. Pluggable settings are a mechanism to allow settings to be stored in separate files. Those files are read at startup and used to modify the default settings. The default location for the dashboard configuration files is ``openstack_dashboard/enabled``, with another directory, ``openstack_dashboard/local/enabled`` for local overrides. Both sets of files will be loaded, but the settings in ``openstack_dashboard/local/enabled`` will overwrite the default ones. The settings are applied in alphabetical order of the filenames. If the same dashboard has configuration files in ``enabled`` and ``local/enabled``, the local name will be used. Note, that since names of python modules can't start with a digit, the files are usually named with a leading underscore and a number, so that you can control their order easily. Before we describe the specific use cases, the following keys can be used in any pluggable settings file: ``ADD_EXCEPTIONS`` ------------------ .. versionadded:: 2014.1(Icehouse) A dictionary of exception classes to be added to ``HORIZON['exceptions']``. ``ADD_INSTALLED_APPS`` ---------------------- .. versionadded:: 2014.1(Icehouse) A list of applications to be prepended to ``INSTALLED_APPS``. This is needed to expose static files from a plugin. ``ADD_ANGULAR_MODULES`` ----------------------- .. versionadded:: 2014.2(Juno) A list of AngularJS modules to be loaded when Angular bootstraps. These modules are added as dependencies on the root Horizon application ``horizon``. ``ADD_JS_FILES`` ---------------------- .. versionadded:: 2014.2(Juno) A list of javascript source files to be included in the compressed set of files that are loaded on every page. This is needed for AngularJS modules that are referenced in ``ADD_ANGULAR_MODULES`` and therefore need to be included in every page. ``ADD_JS_SPEC_FILES`` ---------------------- .. versionadded:: 2015.1(Kilo) A list of javascript spec files to include for integration with the Jasmine spec runner. Jasmine is a behavior-driven development framework for testing JavaScript code. ``ADD_SCSS_FILES`` ---------------------- .. versionadded:: 8.0.0(Liberty) A list of scss files to be included in the compressed set of files that are loaded on every page. We recommend one scss file per dashboard, use @import if you need to include additional scss files for panels. .. _auto_discover_static_files: ``AUTO_DISCOVER_STATIC_FILES`` ------------------------------ .. versionadded:: 8.0.0(Liberty) If set to ``True``, JavaScript files and static angular html template files will be automatically discovered from the `static` folder in each apps listed in ADD_INSTALLED_APPS. JavaScript source files will be ordered based on naming convention: files with extension `.module.js` listed first, followed by other JavaScript source files. JavaScript files for testing will also be ordered based on naming convention: files with extension `.mock.js` listed first, followed by files with extension `.spec.js`. If ADD_JS_FILES and/or ADD_JS_SPEC_FILES are also specified, files manually listed there will be appended to the auto-discovered files. ``DISABLED`` ------------ .. versionadded:: 2014.1(Icehouse) If set to ``True``, this settings file will not be added to the settings. ``UPDATE_HORIZON_CONFIG`` ------------------------- .. versionadded:: 2014.2(Juno) A dictionary of values that will replace the values in ``HORIZON_CONFIG``. Pluggable Settings for Dashboards ================================= .. versionadded:: 2014.1(Icehouse) The following keys are specific to registering a dashboard: ``DASHBOARD`` ------------- .. versionadded:: 2014.1(Icehouse) The slug of the dashboard to be added to ``HORIZON['dashboards']``. Required. ``DEFAULT`` ----------- .. versionadded:: 2014.1(Icehouse) If set to ``True``, this dashboard will be set as the default dashboard. Examples -------- To disable a dashboard locally, create a file ``openstack_dashboard/local/enabled/_40_dashboard-name.py`` with the following content:: DASHBOARD = '' DISABLED = True To add a Tuskar-UI (Infrastructure) dashboard, you have to install it, and then create a file ``openstack_dashboard/local/enabled/_50_tuskar.py`` with:: from tuskar_ui import exceptions DASHBOARD = 'infrastructure' ADD_INSTALLED_APPS = [ 'tuskar_ui.infrastructure', ] ADD_EXCEPTIONS = { 'recoverable': exceptions.RECOVERABLE, 'not_found': exceptions.NOT_FOUND, 'unauthorized': exceptions.UNAUTHORIZED, } Pluggable Settings for Panels ============================= .. versionadded:: 2014.1(Icehouse) The following keys are specific to registering or removing a panel: ``PANEL`` --------- .. versionadded:: 2014.1(Icehouse) The slug of the panel to be added to ``HORIZON_CONFIG``. Required. ``PANEL_DASHBOARD`` ------------------- .. versionadded:: 2014.1(Icehouse) The slug of the dashboard the ``PANEL`` associated with. Required. ``PANEL_GROUP`` --------------- .. versionadded:: 2014.1(Icehouse) The slug of the panel group the ``PANEL`` is associated with. If you want the panel to show up without a panel group, use the panel group "default". ``DEFAULT_PANEL`` ----------------- .. versionadded:: 2014.1(Icehouse) If set, it will update the default panel of the ``PANEL_DASHBOARD``. ``ADD_PANEL`` ------------- .. versionadded:: 2014.1(Icehouse) Python panel class of the ``PANEL`` to be added. ``REMOVE_PANEL`` ---------------- .. versionadded:: 2014.1(Icehouse) If set to ``True``, the PANEL will be removed from PANEL_DASHBOARD/PANEL_GROUP. Examples -------- To add a new panel to the Admin panel group in Admin dashboard, create a file ``openstack_dashboard/local/enabled/_60_admin_add_panel.py`` with the following content:: PANEL = 'plugin_panel' PANEL_DASHBOARD = 'admin' PANEL_GROUP = 'admin' ADD_PANEL = 'test_panels.plugin_panel.panel.PluginPanel' To remove Info panel from Admin panel group in Admin dashboard locally, create a file ``openstack_dashboard/local/enabled/_70_admin_remove_panel.py`` with the following content:: PANEL = 'info' PANEL_DASHBOARD = 'admin' PANEL_GROUP = 'admin' REMOVE_PANEL = True To change the default panel of Admin dashboard to Instances panel, create a file ``openstack_dashboard/local/enabled/_80_admin_default_panel.py`` with the following content:: PANEL = 'instances' PANEL_DASHBOARD = 'admin' PANEL_GROUP = 'admin' DEFAULT_PANEL = 'instances' Pluggable Settings for Panel Groups =================================== .. versionadded:: 2014.1(Icehouse) The following keys are specific to registering a panel group: ``PANEL_GROUP`` --------------- .. versionadded:: 2014.1(Icehouse) The slug of the panel group to be added to ``HORIZON_CONFIG``. Required. ``PANEL_GROUP_NAME`` -------------------- .. versionadded:: 2014.1(Icehouse) The display name of the PANEL_GROUP. Required. ``PANEL_GROUP_DASHBOARD`` ------------------------- .. versionadded:: 2014.1(Icehouse) The slug of the dashboard the ``PANEL_GROUP`` associated with. Required. Examples -------- To add a new panel group to the Admin dashboard, create a file ``openstack_dashboard/local/enabled/_90_admin_add_panel_group.py`` with the following content:: PANEL_GROUP = 'plugin_panel_group' PANEL_GROUP_NAME = 'Plugin Panel Group' PANEL_GROUP_DASHBOARD = 'admin' horizon-9.0.0/doc/source/topics/angularjs.rst0000664000567000056710000003026112701407063022441 0ustar jenkinsjenkins00000000000000===================== AngularJS Topic Guide ===================== .. Note:: This guide is a work in progress. It has been uploaded to encourage faster reviewing and code development in Angular, and to help the community standardize on a set of guidelines. There are notes inline on sections that are likely to change soon, and the docs will be updated promptly after any changes. Getting Started =============== The tooling for AngularJS testing and code linting relies on npm, the node package manager, and thus relies on Node.js. While it is not a prerequisite to developing with Horizon, it is advisable to install Node.js, either through `downloading `_ or `via a package manager `_. Once you have npm available on your system, run ``npm install`` from the horizon root directory. .. _js_code_style: Code Style ========== We currently use the `Angular Style Guide`_ by John Papa as reference material. When reviewing AngularJS code, it is helpful to link directly to the style guide to reinforce a point, e.g. https://github.com/johnpapa/angular-styleguide#style-y024 .. _Angular Style Guide: https://github.com/johnpapa/angular-styleguide ESLint ------ ESLint is a tool for identifying and reporting on patterns in your JS code, and is part of the automated tests run by Jenkins. You can run ESLint from the horizon root directory with ``npm run lint``, or alternatively on a specific directory or file with ``eslint file.js``. Horizon includes a `.eslintrc` in its root directory, that is used by the local tests. An explanation of the options, and details of others you may want to use, can be found in the `ESLint user guide `_. Application Structure ===================== OpenStack Dashboard is an example of a Horizon-based Angular application. Other applications built on the Horizon framework can follow a similar structure. It is composed of two key Angular modules: **app.module.js** - The root of the application. Defines the modules required by the application, and includes modules from its pluggable dashboards. **framework.module.js** - Reusable Horizon components. It is one of the application dependencies. .. _js_file_structure: File Structure ============== Horizon has three kinds of angular code: 1. Specific to one dashboard in the OpenStack Dashboard application 2. Specific to the OpenStack Dashboard application, but reusable by multiple dashboards 3. Reusable by any application based on the Horizon framework When adding code to horizon, consider whether it is dashboard-specific or should be broken out as a reusable utility or widget. Code specific to one dashboard ------------------------------ Code that isn't shared beyond a single dashboard is placed in ``openstack_dashboard/dashboards/mydashboard/static``. Entire dashboards may be enabled or disabled using Horizon's plugin mechanism. Therefore no dashboards other than ``mydashboard`` can safely use this code. The ``openstack_dashboard/dashboards/static`` directory structure determines how the code is deployed and matches the module structure. For example: :: openstack_dashboard/dashboards/identity/static/dashboard/identity/ ├── identity.module.js ├── identity.module.spec.js └── identity.scss Because the code is in ``openstack_dashboard/dashboards/identity`` we know it is specific to just the ``identity`` dashboard and not used by any others. Code shared by multiple dashboards ---------------------------------- Views or utilities needed by multiple dashboards are placed in ``openstack_dashboard/static/app``. For example: :: openstack_dashboard/static/app/core/cloud-services/ ├── cloud-services.js └── cloud-services.spec.js The ``cloud-services`` module is used by panels in multiple dashboards. It cannot be placed within ``openstack_dashboard/dashboards/mydashboard`` because disabling that one dashboard would break others. Therefore, it is included as part of the application ``core`` module. Code in ``app/`` is guaranteed to always be present, even if all other dashboards are disabled. Reusable components ------------------- Finally, components that are easily reused by any application are placed in ``horizon/static/framework/``. These do not contain URLs or business logic that is specific to any application (even the OpenStack Dashboard application). The modal directive ``horizon/static/framework/widgets/modal/`` is a good example of a reusable component. One folder per component ------------------------ Each component should have its own folder, with the code broken up into one JS component per file. (See `Single Responsibility `_ in the style guide). Each folder may include styling (``.scss``), as well as templates(``.html``) and tests (``.spec.js``). You may also include examples, by appending ``.example``. For larger components, such as workflows with multiple steps, consider breaking the code down further. For example, the Launch Instance workflow, has one directory per step. See ``openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/`` SCSS files ---------- The top-level SCSS file in ``openstack_dashboard/static/app/app.scss``. It includes any styling that is part of the application ``core`` and may be reused by multiple dashboards. SCSS files that are specific to a particular dashboard are linked to the application by adding them in that dashboard's enabled file. For example, `_1000_project.py` is the enabled file for the ``Project`` dashboard and includes: :: ADD_SCSS_FILES = [ 'dashboard/project/project.scss', ] Styling files are hierarchical, and include any direct child SCSS files. For example, ``project.scss`` includes the ``workflow`` SCSS file, which in turn includes any launch instance styling: :: @import "workflow/workflow"; This allows the application to easily include all needed styling, simply by including a dashboards top-level SCSS file. Module Structure ================ Horizon Angular modules use names that map to the source code directory structure. This provides namespace isolation for modules and services, which makes dependency injection clearer. It also reduces code conflicts where two different modules define a module, service or constant of the same name. For example: :: openstack_dashboard/dashboards/identity/static/dashboard/identity/ └── identity.module.js The preferred Angular module name in this example is ``horizon.dashboard.identity``. The ``horizon`` part of the module name maps to the ``static`` directory and indicates this is a ``horizon`` based application. ``dashboard.identity`` maps to folders that are created within ``static``. This allows a direct mapping between the angular module name of ``horizon.dashboard.identity`` and the source code directory of ``static\dashboard\identity``. Services and constants within these modules should all start with their module name to avoid dependency injection collisions. For example: :: $provide.constant('horizon.dashboard.identity.basePath', path); Directives do not require the module name but are encouraged to begin with the ``hz`` prefix. For example: :: .directive('hzMagicSearchBar', hzMagicSearchBar); Finally, each module lists its child modules as a dependency. This allows the root module to be included by an application, which will automatically define all child modules. For example: :: .module('horizon.framework', [ 'horizon.framework.conf', 'horizon.framework.util', 'horizon.framework.widgets' ]) ``horizon.framework`` declares a dependency on ``horizon.framework.widgets``, which declares dependencies on each individual widget. This allows the application to access any widget, simply by depending on the top-level ``horizon.framework`` module. Testing ======= 1. Open /jasmine in a browser. The development server can be run with``./run_tests.sh --runserver`` from the horizon root directory. 2. ``npm run test`` from the horizon root directory. The code linting job can be run with ``npm run lint``. For more detailed information, see :doc:`javascript_testing`. Translation (Internationalization and Localization) =================================================== Translations are handled in Transifex, as with Django. They are merged daily with the horizon upstream codebase. See `Translations `_ in the OpenStack wiki to learn more about this process. To translate text in HTML files, you may use the ``translate`` directive or filter. The directive be used as an element, or an attribute: :: // Translate singular, as element Lorem ipsum // Translate singular, as attribute

Lorem ipsum

// Translate plural (attribute only)
apple
// Filter singular // Comments for translators, to add context

File

.. Note:: The filter does not support plural strings. To translate text in JS files, such as Angular controllers, use either ``gettext`` (singular) or ``ngettext`` (plural): :: gettext('apple'); ngettext('apple', 'apples', count); The :ref:`translatability` section contains information about the pseudo translation tool, and how to make sure your translations are working locally. Horizon uses the `angular-gettext `_ library to provide directives and filters for extracting translatable text. Creating your own panel ======================= .. Note:: This section will be extended as standard practices are adopted upstream. Currently, it may be useful to use `this patch `_ and its dependants as an example. .. Note:: Currently, Angular module names must still be manually declared with ``ADD_ANGULAR_MODULES``, even when using automatic file discovery. This section serves as a basic introduction to writing your own panel for horizon, using AngularJS. A panel may be included with the plugin system, or it may be part of the upstream horizon project. Upstream -------- JavaScript files can be discovered automatically, handled manually, or a mix of the two. Where possible, use the automated mechanism. To use the automatic functionality, add:: AUTO_DISCOVER_STATIC_FILES = True to your enabled file (``enabled/.py``). To make this possible, you need to follow some structural conventions: - Static files should be put in a ``static/`` folder, which should be found directly under the folder for the dashboard/panel/panel groups Python package. - JS code that defines an Angular module should be in a file with extension of ``.module.js``. - JS code for testing should be named with extension of ``.mock.js`` and of ``.spec.js``. - Angular templates should have extension of ``.html``. You can read more about the functionality in the :ref:`auto_discover_static_files` section of the settings documentation. To manually add files, add the following arrays and file paths to the enabled file: :: ADD_JS_FILES = [ ... 'path-to/my-angular-code.js', ... ] ADD_JS_SPEC_FILES = [ ... 'path-to/my-angular-code.spec.js', ... ] ADD_ANGULAR_MODULES = [ ... 'angular.module', ... ] Plugins ------- Add a new panel/ panel group/ dashboard (See :doc:`/tutorials/dashboard`). JavaScript file inclusion is the same as the Upstream process. To include external stylesheets, you must ensure that ``ADD_SCSS_FILES`` is defined in your enabled file, and add the relevant filepath, as below: :: ADD_SCSS_FILES = [ ... 'path-to/my-styles.scss', ... ] .. Note:: We highly recommend using a single SCSS file for your plugin. SCSS supports nesting with @import, so if you have multiple files (i.e. per panel styling) it is best to import them all into one, and include that single file. You can read more in the `SASS documentation`_. .. _SASS documentation: http://sass-lang.com/documentation/file.SASS_REFERENCE.html#import horizon-9.0.0/doc/source/topics/install.rst0000664000567000056710000001034112701407063022116 0ustar jenkinsjenkins00000000000000================== Installing Horizon ================== This page covers the basic installation of horizon, the OpenStack dashboard. .. _system-requirements-label: System Requirements =================== * Python 2.7 * Django 1.7 or 1.8 * Minimum required set of running OpenStack services are: * nova: OpenStack Compute * keystone: OpenStack Identity * glance: OpenStack Image service * neutron: OpenStack Networking (unless nova-network is used) * All other services are optional. Horizon supports the following services in the Juno release. If the keystone endpoint for a service is configured, horizon detects it and enables its support automatically. * swift: OpenStack Object Storage * cinder: OpenStack Block Storage * heat: Orchestration * ceilometer: Telemetry * trove: Database service for OpenStack * sahara: Data processing service for OpenStack Installation ============ 1. Compile translation message catalogs for internationalization. This step is not required if you do not need to support languages other than English. GNU ``gettext`` tool is required to compile message catalogs:: $ sudo apt-get install gettext $ ./run_tests.sh --compilemessages This command compiles translation message catalogs within Python virtualenv named ``.venv``. After this step, you can remove ``.venv`` directory safely. 2. Install the horizon python module into your system. Run the following in the top directory:: $ sudo pip install . 3. Create ``openstack_dashboard/local/local_settings.py``. It is usually a good idea to copy ``openstack_dashboard/local/local_settings.py.example`` and edit it. At least we need to customize the following variables in this file. * ``ALLOWED_HOSTS`` (unless ``DEBUG`` is ``True``) * ``OPENSTACK_KEYSTONE_URL`` For more details, please refer to :doc:`deployment` and :doc:`settings`. 4. Optional: Django has a compressor feature that performs many enhancements for the delivery of static files, including standardization and minification/uglification. This processing can be run either online or offline (pre-processed). Letting the compression process occur at runtime will incur processing and memory use when the resources are first requested; doing it ahead of time removes those runtime penalties. If you want the static files to be processed before server runtime, you'll need to configure your local_settings.py to specify ``COMPRESS_OFFLINE = True``, then run the following commands:: $ ./manage.py collectstatic $ ./manage.py compress 5. Set up a web server with WSGI support. It is optional but recommended in production deployments. For example, install Apache web server on Ubuntu:: $ sudo apt-get install apache2 libapache2-mod-wsgi You will either use the provided ``openstack_dashboard/wsgi/django.wsgi`` or generate an ``openstack_dashboard/wsgi/horizon.wsgi`` file with the following command (which detects if you use a virtual environment or not to automatically build an adapted wsgi file):: $ ./manage.py make_web_conf --wsgi Then configure the web server to host OpenStack dashboard via WSGI. For apache2 web server, you may need to create ``/etc/apache2/sites-available/horizon.conf``. The template in devstack is a good example of the file. http://git.openstack.org/cgit/openstack-dev/devstack/tree/files/apache-horizon.template Or, if you previously generated an ``openstack_dashboard/wsgi/horizon.wsgi`` you can automatically generate an apache configuration file:: $ ./manage.py make_web_conf --apache > /etc/apache2/sites-available/horizon.conf Same as above but if you want ssl support: $ ./manage.py make_web_conf --apache --ssl --sslkey=/path/to/ssl/key --sslcert=/path/to/ssl/cert > /etc/apache2/sites-available/horizon.conf 6. Finally, enable the above configuration and restart the web server:: $ sudo a2ensite horizon $ sudo service apache2 restart Next Steps ========== * :doc:`deployment` covers some common questions, concerns and pitfalls you may encounter when deploying horizon in a production environment. * :doc:`settings` lists the available settings for horizon. * :doc:`customizing` describes how to customizing horizon as you want. horizon-9.0.0/doc/source/topics/testing.rst0000664000567000056710000005363612701407071022142 0ustar jenkinsjenkins00000000000000=================== Testing Topic Guide =================== Having good tests in place is absolutely critical for ensuring a stable, maintainable codebase. Hopefully that doesn't need any more explanation. However, what defines a "good" test is not always obvious, and there are a lot of common pitfalls that can easily shoot your test suite in the foot. If you already know everything about testing but are fed up with trying to debug why a specific test failed, you can skip the intro and jump straight to :ref:`debugging_unit_tests`. An overview of testing ====================== There are three main types of tests, each with their associated pros and cons: Unit tests ---------- These are isolated, stand-alone tests with no external dependencies. They are written from the perspective of "knowing the code", and test the assumptions of the codebase and the developer. Pros: * Generally lightweight and fast. * Can be run anywhere, anytime since they have no external dependencies. Cons: * Easy to be lax in writing them, or lazy in constructing them. * Can't test interactions with live external services. Functional tests ---------------- These are generally also isolated tests, though sometimes they may interact with other services running locally. The key difference between functional tests and unit tests, however, is that functional tests are written from the perspective of the user (who knows nothing about the code) and only knows what they put in and what they get back. Essentially this is a higher-level testing of "does the result match the spec?". Pros: * Ensures that your code *always* meets the stated functional requirements. * Verifies things from an "end user" perspective, which helps to ensure a high-quality experience. * Designing your code with a functional testing perspective in mind helps keep a higher-level viewpoint in mind. Cons: * Requires an additional layer of thinking to define functional requirements in terms of inputs and outputs. * Often requires writing a separate set of tests and/or using a different testing framework from your unit tests. * Doesn't offer any insight into the quality or status of the underlying code, only verifies that it works or it doesn't. Integration Tests ----------------- This layer of testing involves testing all of the components that your codebase interacts with or relies on in conjunction. This is equivalent to "live" testing, but in a repeatable manner. Pros: * Catches *many* bugs that unit and functional tests will not. * Doesn't rely on assumptions about the inputs and outputs. * Will warn you when changes in external components break your code. * Will take screenshot of the current page on test fail for easy debug Cons: * Difficult and time-consuming to create a repeatable test environment. * Did I mention that setting it up is a pain? Screenshot directory could be set through horizon.conf file, default value: "./integration_tests_screenshots" So what should I write? ----------------------- A few simple guidelines: #. Every bug fix should have a regression test. Period. #. When writing a new feature, think about writing unit tests to verify the behavior step-by-step as you write the feature. Every time you'd go to run your code by hand and verify it manually, think "could I write a test to do this instead?". That way when the feature is done and you're ready to commit it you've already got a whole set of tests that are more thorough than anything you'd write after the fact. #. Write tests that hit every view in your application. Even if they don't assert a single thing about the code, it tells you that your users aren't getting fatal errors just by interacting with your code. What makes a good unit test? ============================ Limiting our focus just to unit tests, there are a number of things you can do to make your unit tests as useful, maintainable, and unburdensome as possible. Test data --------- Use a single, consistent set of test data. Grow it over time, but do everything you can not to fragment it. It quickly becomes unmaintainable and perniciously out-of-sync with reality. Make your test data as accurate to reality as possible. Supply *all* the attributes of an object, provide objects in all the various states you may want to test. If you do the first suggestion above *first* it makes the second one far less painful. Write once, use everywhere. To make your life even easier, if your codebase doesn't have a built-in ORM-like function to manage your test data you can consider building (or borrowing) one yourself. Being able to do simple retrieval queries on your test data is incredibly valuable. Mocking ------- Mocking is the practice of providing stand-ins for objects or pieces of code you don't need to test. While convenient, they should be used with *extreme* caution. Why? Because overuse of mocks can rapidly land you in a situation where you're not testing any real code. All you've done is verified that your mocking framework returns what you tell it to. This problem can be very tricky to recognize, since you may be mocking things in ``setUp`` methods, other modules, etc. A good rule of thumb is to mock as close to the source as possible. If you have a function call that calls an external API in a view , mock out the external API, not the whole function. If you mock the whole function you've suddenly lost test coverage for an entire chunk of code *inside* your codebase. Cut the ties cleanly right where your system ends and the external world begins. Similarly, don't mock return values when you could construct a real return value of the correct type with the correct attributes. You're just adding another point of potential failure by exercising your mocking framework instead of real code. Following the suggestions for testing above will make this a lot less burdensome. Assertions and verification --------------------------- Think long and hard about what you really want to verify in your unit test. In particular, think about what custom logic your code executes. A common pitfall is to take a known test object, pass it through your code, and then verify the properties of that object on the output. This is all well and good, except if you're verifying properties that were untouched by your code. What you want to check are the pieces that were *changed*, *added*, or *removed*. Don't check the object's id attribute unless you have reason to suspect it's not the object you started with. But if you added a new attribute to it, be damn sure you verify that came out right. It's also very common to avoid testing things you really care about because it's more difficult. Verifying that the proper messages were displayed to the user after an action, testing for form errors, making sure exception handling is tested... these types of things aren't always easy, but they're extremely necessary. To that end, Horizon includes several custom assertions to make these tasks easier. :meth:`~openstack_dashboard.test.helpers.TestCase.assertNoFormErrors`, :meth:`~horizon.test.helpers.TestCase.assertMessageCount`, and :meth:`~horizon.test.helpers.TestCase.assertNoMessages` all exist for exactly these purposes. Moreover, they provide useful output when things go wrong so you're not left scratching your head wondering why your view test didn't redirect as expected when you posted a form. .. _debugging_unit_tests: Debugging Unit Tests ==================== Tips and tricks --------------- #. Use :meth:`~openstack_dashboard.test.helpers.TestCase.assertNoFormErrors` immediately after your ``client.post`` call for tests that handle form views. This will immediately fail if your form POST failed due to a validation error and tell you what the error was. #. Use :meth:`~horizon.test.helpers.TestCase.assertMessageCount` and :meth:`~horizon.test.helpers.TestCase.assertNoMessages` when a piece of code is failing inexplicably. Since the core error handlers attach user-facing error messages (and since the core logging is silenced during test runs) these methods give you the dual benefit of verifying the output you expect while clearly showing you the problematic error message if they fail. #. Use Python's ``pdb`` module liberally. Many people don't realize it works just as well in a test case as it does in a live view. Simply inserting ``import pdb; pdb.set_trace()`` anywhere in your codebase will drop the interpreter into an interactive shell so you can explore your test environment and see which of your assumptions about the code isn't, in fact, flawlessly correct. #. If the error is in the Selenium test suite, you're likely getting very little information about the error. To increase the information provided to you, edit ``horizon/test/settings.py`` to set ``DEBUG = True`` and set the logging level to 'DEBUG' for the default 'test' logger. Also, add a logger config for Django:: }, 'loggers': { + 'django': { + 'handlers': ['test'], + 'propagate': False, + }, 'django.db.backends': { Common pitfalls --------------- There are a number of typical (and non-obvious) ways to break the unit tests. Some common things to look for: #. Make sure you stub out the method exactly as it's called in the code being tested. For example, if your real code calls ``api.keystone.tenant_get``, stubbing out ``api.tenant_get`` (available for legacy reasons) will fail. #. When defining the expected input to a stubbed call, make sure the arguments are *identical*, this includes ``str`` vs. ``int`` differences. #. Make sure your test data are completely in line with the expected inputs. Again, ``str`` vs. ``int`` or missing properties on test objects will kill your tests. #. Make sure there's nothing amiss in your templates (particularly the ``{% url %}`` tag and its arguments). This often comes up when refactoring views or renaming context variables. It can easily result in errors that you might not stumble across while clicking around the development server. #. Make sure you're not redirecting to views that no longer exist, e.g. the ``index`` view for a panel that got combined (such as instances & volumes). #. Make sure your mock calls are in order before calling ``mox.ReplayAll``. The order matters. #. Make sure you repeat any stubbed out method calls that happen more than once. They don't automatically repeat, you have to explicitly define them. While this is a nuisance, it makes you acutely aware of how many API calls are involved in a particular function. Understanding the output from ``mox`` ------------------------------------- Horizon uses ``mox`` as its mocking framework of choice, and while it offers many nice features, its output when a test fails can be quite mysterious. Unexpected Method Call ~~~~~~~~~~~~~~~~~~~~~~ This occurs when you stubbed out a piece of code, and it was subsequently called in a way that you didn't specify it would be. There are two reasons this tends to come up: #. You defined the expected call, but a subtle difference crept in. This may be a string versus integer difference, a string versus unicode difference, a slightly off date/time, or passing a name instead of an id. #. The method is actually being called *multiple times*. Since mox uses a call stack internally, it simply pops off the expected method calls to verify them. That means once a call is used once, it's gone. An easy way to see if this is the case is simply to copy and paste your method call a second time to see if the error changes. If it does, that means your method is being called more times than you think it is. Expected Method Never Called ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This one is the opposite of the unexpected method call. This one means you told mox to expect a call and it didn't happen. This is almost always the result of an error in the conditions of the test. Using the :meth:`~openstack_dashboard.test.helpers.TestCase.assertNoFormErrors` and :meth:`~horizon.test.helpers.TestCase.assertMessageCount` will make it readily apparent what the problem is in the majority of cases. If not, then use ``pdb`` and start interrupting the code flow to see where things are getting off track. Integration tests in Horizon ============================ The integration tests currently live in the Horizon repository, see `here`_, which also contains instructions on how to run the tests. To make integration tests more understandable and maintainable, the Page Object pattern is used throughout them. Horizon repository also provides two shell `scripts`_, which are executed in pre_test_hook and post_test_hook respectively. Pre hook is generally used for modifying test environment, while post hook is used for running actual integration tests with tox and collecting test artifacts. Thanks to the incorporating all modifications to tests into Horizon repository, one can alter both tests and test environment and see the immediate results in Jenkins job output. .. _here: https://github.com/openstack/horizon/tree/master/openstack_dashboard/test/integration_tests .. _scripts: https://github.com/openstack/horizon/tree/master/tools/gate/integration Page Object pattern ------------------- Within any web application's user interface (UI) there are areas that the tests interact with. A Page Object simply models these as objects within the test code. This reduces the amount of duplicated code; if the UI changes, the fix needs only be applied in one place. Page Objects can be thought of as facing in two directions simultaneously. Facing towards the developer of a test, they represent the services offered by a particular page. Facing away from the developer, they should be the only thing that has a deep knowledge of the structure of the HTML of a page (or part of a page). It is simplest to think of the methods on a Page Object as offering the "services" that a page offers rather than exposing the details and mechanics of the page. As an example, think of the inbox of any web-based email system. Amongst the services that it offers are typically the ability to compose a new email, to choose to read a single email, and to list the subject lines of the emails in the inbox. How these are implemented should not matter to the test. Writing reusable and maintainable Page Objects ---------------------------------------------- Because the main idea is to encourage the developer of a test to try and think about the services that they are interacting with rather than the implementation, Page Objects should seldom expose the underlying WebDriver instance. To facilitate this, methods on the Page Object should return other Page Objects. This means that we can effectively model the user's journey through the application. Another important thing to mention is that a Page Object need not represent an entire page. It may represent a section that appears many times within a site or page, such as site navigation. The essential principle is that there is only one place in your test suite with knowledge of the structure of the HTML of a particular (part of a) page. With this in mind, a test developer builds up regions that become reusable components (`example of a base form`_). These properties can then be redefined or overridden (e.g. selectors) in the actual pages (subclasses) (`example of a tabbed form`_). The page objects are read-only and define the read-only and clickable elements of a page, which work to shield the tests. For instance, from the test perspective, if "Logout" used to be a link but suddenly becomes an option in a drop-down menu, there are no changes (in the test itself) because it still simply calls the "click_on_logout" action method. This approach has two main aspects: * The classes with the actual tests should be as readable as possible * The other parts of the testing framework should be as much about data as possible, so that if the CSS etc. changes you only need to change that one property. If the flow changes, only the action method should need to change. There is little that is Selenium-specific in the Pages, except for the properties. There is little coupling between the tests and the pages. Writing the tests becomes like writing out a list of steps (by using the previously mentioned action methods). One of the key points, particularly important for this kind of UI driven testing is to isolate the tests from what is behind them. .. _example of a base form: https://github.com/openstack/horizon/blob/8.0.0/openstack_dashboard/test/integration_tests/regions/forms.py#L250 .. _example of a tabbed form: https://github.com/openstack/horizon/blob/8.0.0/openstack_dashboard/test/integration_tests/regions/forms.py#L322 List of references ------------------ * https://wiki.openstack.org/wiki/Horizon/Testing/UI#Page_Object_Pattern_.28Selected_Approach.29 * https://wiki.mozilla.org/QA/Execution/Web_Testing/Docs/Automation/StyleGuide#Page_Objects * https://code.google.com/p/selenium/wiki/PageObjects Debugging integration tests =========================== Even perfectly designed Page Objects are not a guarantee that your integration test will not ever fail. This can happen due to different causes: The first and most anticipated kind of failure is the inability to perform a testing scenario by a living person simply because some OpenStack service or Horizon itself prevents them from doing so. This is exactly the kind that integration tests are designed to catch. Let us call them "good" failures. All other kinds of failures are unwanted and could be roughly split into the two following categories: #. The failures that occur due to changes in application's DOM. some CSS/ Xpath selectors no longer matching Horizon app's DOM. The usual signature for that kind of failures is having a DOM changing patch for which the test job fails with a message like this `selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: {"method":"css selector","selector":"div.modal-dialog"}`. If you find yourself in such a situation, you should fix the Page Object selectors according to the DOM changes you made. #. Unfortunately it is still quite possible to get the above error for a patch which didn't implement any DOM changes. Among the reasons of such behavior observed in past were: * Integration tests relying on relative ordering of form fields and table actions that broke with the addition of a new field. This issue should be fixed by now, but may reappear in future for different entities. * Integration tests relying on popups disappearing by the time a specific action needs to be taken (or not existing at all). This expectation turned out to be very fragile, since the speed of tests execution by Jenkins workers may change independently of integration test code (hence, popups disappear too late to free the way for the next action). The unexpected (both too long and too short) timeouts aren't limited to just popups, but apply to every situation when the element state transition is not instant (like opening an external link, going to another page in Horizon, waiting for button to become active, waiting for a table row to change its state). Luckily, most transitions of "element becomes visible/ emerge to existence from non-existence" kind are already bulletproofed using `implicit_wait` parameter in `integration_tests/horizon.conf` file. Selenium just waits for specified amount of seconds for an element to become visible (if it's not already visible) giving up when it exceeds (with the above error). Also it's worth mentioning `explicit_wait` parameter which is considered when the selenium `wait_until` method is involved (and it is used, e.g. in waiting for spinner and messages popups to disappear). An inconvenient thing about reading test results in the `console.html` file attached to every `gate-horizon-dsvm-integration` finished job is that the test failure may appear either as failure (assertion failed), or as error (expected element didn't show up). In both cases an inquirer should suspect a legitimate failure first (i.e., treat errors as failures). Unfortunately, no clear method exists for the separation of "good" from from "bad" failures. Each case is unique and full of mysteries. The Horizon testing mechanism tries to alleviate this ambiguity by providing several facilities to aid in failure investigation: * First there comes a screenshot made for every failed test (in a separate folder, on a same level as `console.html`) - almost instant snapshot of a screen on the moment of failure (*almost* sometimes matters, especially in a case of popups that hang on a screen for a limited time); * Then the patient inquirer may skim through the vast innards of `console.html`, looking at browser log first (all javascript and css errors should come there), * Then looking at a full textual snapshot of a page for which test failed (sometimes it gives a more precise picture than a screenshot), * And finally looking at test error stacktrace (most useful) and a lengthy output of requests/ responses with a selenium server. The last log sometimes might tell us how long a specific web element was polled before failing (in case of `implicit_wait` there should be a series of requests to the same element). The best way to solve the cause of test failure is running and debugging the troublesome test locally. You could use `pdb` or Python IDE of your choice to stop test execution in arbitrary points and examining various Page Objects attributes to understand what they missed. Looking at the real page structure in browser developer tools also could explain why the test fails. Sometimes it may be worth to place breakpoints in JavaScript code (provided that static is served uncompressed) to examine the objects of interest. If it takes long, you may also want to increase the webdriver's timeout so it will not close browser windows forcefully. Finally, sometimes it may make sense to examine the contents of `logs` directory, especially apache logs - but that is mostly the case for the "good" failures.horizon-9.0.0/doc/source/topics/tables.rst0000664000567000056710000003553412701407063021735 0ustar jenkinsjenkins00000000000000====================== DataTables Topic Guide ====================== Horizon provides the :mod:`horizon.tables` module to provide a convenient, reusable API for building data-driven displays and interfaces. The core components of this API fall into three categories: ``DataTables``, ``Actions``, and ``Class-based Views``. .. seealso:: For a detailed API information check out the :doc:`DataTables Reference Guide `. Tables ====== The majority of interface in a dashboard-style interface ends up being tabular displays of the various resources the dashboard interacts with. The :class:`~horizon.tables.DataTable` class exists so you don't have to reinvent the wheel each time. Creating your own tables ------------------------ Creating a table is fairly simple: #. Create a subclass of :class:`~horizon.tables.DataTable`. #. Define columns on it using :class:`~horizon.tables.Column`. #. Create an inner ``Meta`` class to contain the special options for this table. #. Define any actions for the table, and add them to :attr:`~horizon.tables.DataTableOptions.table_actions` or :attr:`~horizon.tables.DataTableOptions.row_actions`. Examples of this can be found in any of the ``tables.py`` modules included in the reference modules under ``horizon.dashboards``. Connecting a table to a view ---------------------------- Once you've got your table set up the way you like it, the next step is to wire it up to a view. To make this as easy as possible Horizon provides the :class:`~horizon.tables.DataTableView` class-based view which can be subclassed to display your table with just a couple lines of code. At its simplest, it looks like this:: from horizon import tables from .tables import MyTable class MyTableView(tables.DataTableView): table_class = MyTable template_name = "my_app/my_table_view.html" def get_data(self): return my_api.objects.list() In the template you would just need to include the following to render the table:: {{ table.render }} That's it! Easy, right? Actions ======= Actions comprise any manipulations that might happen on the data in the table or the table itself. For example, this may be the standard object CRUD, linking to related views based on the object's id, filtering the data in the table, or fetching updated data when appropriate. When actions get run -------------------- There are two points in the request-response cycle in which actions can take place; prior to data being loaded into the table, and after the data is loaded. When you're using one of the pre-built class-based views for working with your tables the pseudo-workflow looks like this: #. The request enters view. #. The table class is instantiated without data. #. Any "preemptive" actions are checked to see if they should run. #. Data is fetched and loaded into the table. #. All other actions are checked to see if they should run. #. If none of the actions have caused an early exit from the view, the standard response from the view is returned (usually the rendered table). The benefit of the multi-step table instantiation is that you can use preemptive actions which don't need access to the entire collection of data to save yourself on processing overhead, API calls, etc. Basic actions ------------- At their simplest, there are three types of actions: actions which act on the data in the table, actions which link to related resources, and actions that alter which data is displayed. These correspond to :class:`~horizon.tables.Action`, :class:`~horizon.tables.LinkAction`, and :class:`~horizon.tables.FilterAction`. Writing your own actions generally starts with subclassing one of those action classes and customizing the designated attributes and methods. Shortcut actions ---------------- There are several common tasks for which Horizon provides pre-built shortcut classes. These include :class:`~horizon.tables.BatchAction`, and :class:`~horizon.tables.DeleteAction`. Each of these abstracts away nearly all of the boilerplate associated with writing these types of actions and provides consistent error handling, logging, and user-facing interaction. It is worth noting that ``BatchAction`` and ``DeleteAction`` are extensions of the standard ``Action`` class. Some ``BatchAction`` or ``DeleteAction`` classes may cause some unrecoverable results, like deleted images or unrecoverable instances. It may be helpful to specify specific help_text to explain the concern to the user, such as "Deleted images are not recoverable". Preemptive actions ------------------ Action classes which have their :attr:`~horizon.tables.Action.preempt` attribute set to ``True`` will be evaluated before any data is loaded into the table. As such, you must be careful not to rely on any table methods that require data, such as :meth:`~horizon.tables.DataTable.get_object_display` or :meth:`~horizon.tables.DataTable.get_object_by_id`. The advantage of preemptive actions is that you can avoid having to do all the processing, API calls, etc. associated with loading data into the table for actions which don't require access to that information. Policy checks on actions ------------------------ The :attr:`~horizon.tables.Action.policy_rules` attribute, when set, will validate access to the action using the policy rules specified. The attribute is a list of scope/rule pairs. Where the scope is the service type defining the rule and the rule is a rule from the corresponding service policy.json file. The format of :attr:`horizon.tables.Action.policy_rules` looks like:: (("identity", "identity:get_user"),) Multiple checks can be made for the same action by merely adding more tuples to the list. The policy check will use information stored in the session about the user and the result of :meth:`~horizon.tables.Action.get_policy_target` (which can be overridden in the derived action class) to determine if the user can execute the action. If the user does not have access to the action, the action is not added to the table. If :attr:`~horizon.tables.Action.policy_rules` is not set, no policy checks will be made to determine if the action should be visible and will be displayed solely based on the result of :meth:`~horizon.tables.Action.allowed`. For more information on policy based Role Based Access Control see: :doc:`Horizon Policy Enforcement (RBAC: Role Based Access Control) `. Table Cell filters (decorators) =============================== DataTable displays lists of objects in rows and object attributes in cell. How should we proceed, if we want to decorate some column, e.g. if we have column ``memory`` which returns a number e.g. 1024, and we want to show something like 1024.00 GB inside table? Decorator pattern ----------------- The clear anti-pattern is defining the new attributes on object like ``ram_float_format_2_gb`` or to tweak a DataTable in any way for displaying purposes. The cleanest way is to use ``filters``. Filters are decorators, following GOF ``Decorator pattern``. This way ``DataTable logic`` and ``displayed object logic`` are correctly separated from ``presentation logic`` of the object inside of the various tables. And therefore the filters are reusable in all tables. Filter function --------------- Horizon DatablesTable takes a tuple of pointers to filter functions or anonymous lambda functions. When displaying a ``Cell``, ``DataTable`` takes ``Column`` filter functions from left to right, using the returned value of the previous function as a parameter of the following function. Then displaying the returned value of the last filter function. A valid filter function takes one parameter and returns the decorated value. So e.g. these are valid filter functions :: # Filter function. def add_unit(v): return str(v) + " GB" # Or filter lambda function. lambda v: str(v) + " GB" # This is also a valid definition of course, although for the change of the # unit parameter, function has to be wrapped by lambda # (e.g. floatformat function example below). def add_unit(v, unit="GB"): return str(v) + " " + unit Using filters in DataTable column --------------------------------- DataTable takes tuple of filter functions, so e.g. this is valid decorating of a value with float format and with unit :: ram = tables.Column( "ram", verbose_name=_('Memory'), filters=(lambda v: floatformat(v, 2), add_unit)) It always takes tuple, so using only one filter would look like this :: filters=(lambda v: floatformat(v, 2),) The decorated parameter doesn't have to be only a string or number, it can be anything e.g. list or an object. So decorating of object, that has attributes value and unit would look like this :: ram = tables.Column( "ram", verbose_name=_('Memory'), filters=(lambda x: getattr(x, 'value', '') + " " + getattr(x, 'unit', ''),)) Available filters ----------------- There are a load of filters, that can be used, defined in django already: https://github.com/django/django/blob/master/django/template/defaultfilters.py So it's enough to just import and use them, e.g. :: from django.template import defaultfilters as filters # code omitted filters=(filters.yesno, filters.capfirst) from django.template.defaultfilters import timesince from django.template.defaultfilters import title # code omitted filters=(parse_isotime, timesince) Inline editing ============== Table cells can be easily upgraded with in-line editing. With use of django.form.Field, we are able to run validations of the field and correctly parse the data. The updating process is fully encapsulated into table functionality, communication with the server goes through AJAX in JSON format. The javascript wrapper for inline editing allows each table cell that has in-line editing available to: #. Refresh itself with new data from the server. #. Display in edit mod. #. Send changed data to server. #. Display validation errors. There are basically 3 things that need to be defined in the table in order to enable in-line editing. Fetching the row data --------------------- Defining an ``get_data`` method in a class inherited from ``tables.Row``. This method takes care of fetching the row data. This class has to be then defined in the table Meta class as ``row_class = UpdateRow``. Example:: class UpdateRow(tables.Row): # this method is also used for automatic update of the row ajax = True def get_data(self, request, project_id): # getting all data of all row cells project_info = api.keystone.tenant_get(request, project_id, admin=True) return project_info Updating changed cell data -------------------------- Define an ``update_cell`` method in the class inherited from ``tables.UpdateAction``. This method takes care of saving the data of the table cell. There can be one class for every cell thanks to the ``cell_name`` parameter. This class is then defined in tables column as ``update_action=UpdateCell``, so each column can have its own updating method. Example:: class UpdateCell(tables.UpdateAction): def allowed(self, request, project, cell): # Determines whether given cell or row will be inline editable # for signed in user. return api.keystone.keystone_can_edit_project() def update_cell(self, request, project_id, cell_name, new_cell_value): # in-line update project info try: project_obj = datum # updating changed value by new value setattr(project_obj, cell_name, new_cell_value) # sending new attributes back to API api.keystone.tenant_update( request, project_id, name=project_obj.name, description=project_obj.description, enabled=project_obj.enabled) except Conflict: # Validation error for naming conflict, raised when user # choose the existing name. We will raise a # ValidationError, that will be sent back to the client # browser and shown inside of the table cell. message = _("This name is already taken.") raise ValidationError(message) except: # Other exception of the API just goes through standard # channel exceptions.handle(request, ignore=True) return False return True Defining a form_field for each Column that we want to be in-line edited. ------------------------------------------------------------------------ Form field should be ``django.form.Field`` instance, so we can use django validations and parsing of the values sent by POST (in example validation ``required=True`` and correct parsing of the checkbox value from the POST data). Form field can be also ``django.form.Widget`` class, if we need to just display the form widget in the table and we don't need Field functionality. Then connecting ``UpdateRow`` and ``UpdateCell`` classes to the table. Example:: class TenantsTable(tables.DataTable): # Adding html text input for inline editing, with required validation. # HTML form input will have a class attribute tenant-name-input, we # can define here any HTML attribute we need. name = tables.Column('name', verbose_name=_('Name'), form_field=forms.CharField(required=True), form_field_attributes={'class':'tenant-name-input'}, update_action=UpdateCell) # Adding html textarea without required validation. description = tables.Column(lambda obj: getattr(obj, 'description', None), verbose_name=_('Description'), form_field=forms.CharField( widget=forms.Textarea(), required=False), update_action=UpdateCell) # Id will not be inline edited. id = tables.Column('id', verbose_name=_('Project ID')) # Adding html checkbox, that will be shown inside of the table cell with # label enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True, form_field=forms.BooleanField( label=_('Enabled'), required=False), update_action=UpdateCell) class Meta: name = "tenants" verbose_name = _("Projects") # Connection to UpdateRow, so table can fetch row data based on # their primary key. row_class = UpdateRow horizon-9.0.0/doc/source/topics/styling.rst0000664000567000056710000001063212701407063022144 0ustar jenkinsjenkins00000000000000========================= Styling in Horizon (SCSS) ========================= Horizon uses `SCSS`_ (not to be confused with Sass) to style its HTML. This guide is targeted at developers adding code to upstream Horizon. For information on creating your own branding/theming, see :doc:`customizing`. .. _SCSS: http://sass-lang.com/guide Code Layout =========== The base SCSS can be found at ``openstack_dashboard/static/dashboard/scss/``. This directory should **only** contain the minimal styling for functionality; code that isn't configurable by themes. ``horizon.scss`` is a top level file that imports from the ``components/`` directory, as well as other base styling files; potentially some basic page layout rules that Horizon relies on to function. .. Note:: Currently, a great deal of theming is also kept in the ``horizon.scss`` file in this directory, but that will be reduced as we proceed with the new code design. Horizon's ``default`` theme stylesheets can be found at ``openstack_dashboard/themes/default/``. :: ├── _styles.scss ├── _variables.scss ├── bootstrap/ └── ... └── horizon/ └── ... The top level ``_styles.scss`` and ``_variables.scss`` contain imports from the ``bootstrap`` and ``horizon`` directories. The "bootstrap" directory ------------------------- This directory contains overrides and customization of Bootstrap variables, and markup used by Bootstrap components. This should **only** override existing Bootstrap content. For examples of these components, see the `Theme Preview Page`_. :: bootstrap/ ├── _styles.scss ├── _variables.scss └── components/ ├── _component_0.scss ├── _component_1.scss └── ... - ``_styles.scss`` imports the SCSS defined for each component. - ``_variables.scss`` contains the definitions for every Bootstrap variable. These variables can be altered to affect the look and feel of Horizon's default theme. - The ``components`` directory contains overrides for Bootstrap components, such as tables or navbars. The "horizon" directory ----------------------- This directory contains SCSS that is absolutely specific to Horizon; code here should **not** override existing Bootstrap content, such as variables and rules. :: horizon/ ├── _styles.scss ├── _variables.scss └── components/ ├── _component_0.scss ├── _component_1.scss └── ... - ``_styles.scss`` imports the SCSS defined for each component. It may also contain some minor styling overrides. - ``_variables.scss`` contains variable definitions that are specific to the horizon theme. This should **not** override any bootstrap variables, only define new ones. You can however, inherit bootstrap variables for reuse (and are encouraged to do so where possible). - The ``components`` directory contains styling for each individual component defined by Horizon, such as the sidebar or pie charts. Adding new SCSS =============== To keep Horizon easily themable, there are several code design guidelines that should be adhered to: - Reuse Bootstrap variables where possible. This allows themes to influence styling by simply overriding a few existing variables, instead of rewriting large chunks of the SCSS files. - If you are unable to use existing variables - such as for very specific functionality - keep the new rules as specific as possible to your component so they do not cause issues in unexpected places. - Check if existing components suit your use case. There may be existing components defined by Bootstrap or Horizon that can be reused, rather than writing new ones. Theme Preview Page ================== When the :ref:`DEBUG ` setting is set to ``True``, the Developer dashboard will be present in Horizon's side nav. The Bootstrap Theme Preview panel contains examples of all stock Bootstrap markup with the currently applied theme, as well as source code for replicating them; click the ```` symbol when hovering over a component. Alternate Theme =============== A second theme is provided by default at ``openstack_dashboard/themes/material/``. When adding new SCSS to horizon, you should check that it does not interfere with the Material theme. Images of how the Material theme should look can be found at https://bootswatch.com/paper/. This theme is now configured to run as the alternate theme within Horizon. horizon-9.0.0/doc/source/topics/policy.rst0000664000567000056710000001573512701407063021763 0ustar jenkinsjenkins00000000000000============================================================ Horizon Policy Enforcement (RBAC: Role Based Access Control) ============================================================ Introduction ============ Horizon's policy enforcement builds on the oslo_policy engine. The basis of which is ``openstack_auth/policy.py``. Services in OpenStack use the oslo policy engine to define policy rules to limit access to APIs based primarily on role grants and resource ownership. The Keystone v3 API provides an interface for creating/reading/updating policy files in the keystone database. However, at this time services do not load the policy files into Keystone. Thus, the implementation in Horizon is based on copies of policy.json files found in the service's source code. The long-term goal is to read/utilize/update these policy files in Horizon. The service rules files are loaded into the policy engine to determine access rights to actions and service APIs. Horizon Settings ================ There are a few settings that must be in place for the Horizon policy engine to work. ``POLICY_FILES_PATH`` --------------------- Default: ``os.path.join(ROOT_PATH, "conf")`` Specifies where service based policy files are located. These are used to define the policy rules actions are verified against. This value must contain the files listed in ``POLICY_FILES`` or all policy checks will pass. .. note:: The path to deployment specific policy files can be specified in ``local_settings.py`` to override the default location. ``POLICY_FILES`` ---------------- Default: ``{'identity': 'keystone_policy.json', 'compute': 'nova_policy.json'}`` This should essentially be the mapping of the contents of ``POLICY_FILES_PATH`` to service types. When policy.json files are added to the directory ``POLICY_FILES_PATH``, they should be included here too. Without this mapping, there is no way to map service types with policy rules, thus two policy.json files containing a "default" rule would be ambiguous. .. note:: Deployment specific policy files can be specified in ``local_settings.py`` to override the default policy files. It is imperative that these policy files match those deployed in the target OpenStack installation. Otherwise, the displayed actions and the allowed action will not match. ``POLICY_CHECK_FUNCTION`` ------------------------- Default: ``policy.check`` This value should not be changed, although removing it would be a means to bypass all policy checks. How user's roles are determined =============================== Each policy check uses information about the user stored on the request to determine the user's roles. This information was extracted from the scoped token received from Keystone when authenticating. Entity ownership is also a valid role. To verify access to specific entities like a project, the target must be specified. See the section :ref:`rule targets ` later in this document. How to Utilize RBAC =================== The primary way to add role based access control checks to panels is in the definition of table actions. When implementing a derived action class, setting the :attr:`~horizon.tables.Action.policy_rules` attribute to valid policy rules will force a policy check before the :meth:`horizon.tables.Action.allowed` method is called on the action. These rules are defined in the policy files pointed to by ``POLICY_PATH`` and ``POLICY_FILES``. The rules are role based, where entity owner is also a role. The format for the ``policy_rules`` is a list of two item tuples. The first component of the tuple is the scope of the policy rule, this is the service type. This informs the policy engine which policy file to reference. The second component is the rule to enforce from the policy file specified by the scope. An example tuple is:: ("identity", "identity:get_user") x tuples can be added to enforce x rules. .. note:: If a rule specified is not found in the policy file, the policy check will return False and the action will not be allowed. The secondary way to add a role based check is to directly use the :meth:`~openstack_dashboard.policy.check` method. The method takes a list of actions, same format as the :attr:`~horizon.tables.Action.policy_rules` attribute detailed above; the current request object; and a dictionary of action targets. This is the method that :class:`horizon.tables.Action` class utilizes. Examples look like:: from openstack_dashboard import policy allowed = policy.check((("identity", "identity:get_user"), ("identity", "identity:get_project"),), request) can_see = policy.check((("identity", "identity:get_user"),), request, target={"domain_id": domainId}) .. note:: Any time multiple rules are specified in a single `policy.check` method call, the result is the logical `and` of each rule check. So, if any rule fails verification, the result is `False`. The third way to add a role based check is in javascript files. Use the method 'ifAllowed()' in file 'openstack_dashboard.static.app.core.policy.service.js'. The method takes a list of actions, similar format with the :attr:`~horizon.tables.Action.policy_rules` attribute detailed above. An Example looks like:: angular .module('horizon.dashboard.identity.users') .controller('identityUsersTableController', identityUsersTableController); identityUsersTableController.$inject = [ 'horizon.app.core.openstack-service-api.policy', ]; function identityUsersTableController(toast, gettext, policy, keystone) { var rules = [['identity', 'identity:list_users']]; policy.ifAllowed({ rules: rules }).then(policySuccess, policyFailed); } The fourth way to add a role based check is in html files. Use angular directive 'hz-if-policies' in file 'openstack_dashboard.static.app.core.cloud-services.hz-if-policies-directive.js'. Assume you have the following policy defined in your angular controller:: ctrl.policy = { rules: [["identity", "identity:update_user"]] } Then in your HTML, use it like so::
I am visible if the policy is allowed!
.. _rule_targets: Rule Targets ============ Some rules allow access if the user owns the entity. Policy check targets specify particular entities to check for user ownership. The target parameter to the :meth:`~openstack_dashboard.policy.check` method is a simple dictionary. For instance, the target for checking access a project looks like:: {"project_id": "0905760626534a74979afd3f4a9d67f1"} If the value matches the ``project_id`` to which the user's token is scoped, then access is allowed. When deriving the :class:`horizon.tables.Action` class for use in a table, if a policy check is desired for a particular target, the implementer should override the :meth:`horizon.tables.Action.get_policy_target` method. This allows a programmatic way to specify the target based on the current datum. The value returned should be the target dictionary. horizon-9.0.0/doc/source/topics/deployment.rst0000664000567000056710000002261312701407071022634 0ustar jenkinsjenkins00000000000000================= Deploying Horizon ================= This guide aims to cover some common questions, concerns and pitfalls you may encounter when deploying Horizon in a production environment. .. seealso:: :doc:`settings` .. note:: The Service Catalog returned by the Identity Service after a user has successfully authenticated determines the dashboards and panels that will be available within the OpenStack Dashboard. If you are not seeing a particular service you expected (e.g. Object Storage/Swift or Networking/Neutron) make sure your Service Catalog is configured correctly. Prior to the Essex release of Horizon these features were controlled by individual settings in the ``local_settings.py`` file. This code has been long-since removed and those pre-Essex settings have no impact now. Configure Your Identity Service Host ==================================== The one thing you *must* do in order to run Horizon is to specify the host for your OpenStack Identity Service endpoint. To do this, set the value of the ``OPENSTACK_HOST`` settings in your ``local_settings.py`` file. Logging ======= Logging is an important concern for production deployments, and the intricacies of good logging configuration go far beyond what can be covered here. However there are a few points worth noting about the logging included with Horizon, how to customize it, and where other components may take over: * Horizon's logging uses Django's logging configuration mechanism, which can be customized in your ``local_settings.py`` file through the ``LOGGING`` dictionary. * Horizon's default logging example sets the log level to ``"INFO"``, which is a reasonable choice for production deployments. For development, however, you may want to change the log level to ``"DEBUG"``. * Horizon also uses a number of 3rd-party clients which log separately. The log level for these can still be controlled through Horizon's ``LOGGING`` config, however behaviors may vary beyond Horizon's control. * For more information regarding configuring logging in Horizon, please read the `Django logging directive`_ and the `Python logging directive`_ documentation. Horizon is built on Python and Django. .. _Django logging directive: https://docs.djangoproject.com/en/1.5/topics/logging .. _Python logging directive: http://docs.python.org/2/library/logging.html .. warning:: At this time there is `a known bug in python-keystoneclient`_ where it will log the complete request body of any request sent to Keystone through it (including logging passwords in plain text) when the log level is set to ``"DEBUG"``. If this behavior is not desired, make sure your log level is ``"INFO"`` or higher. .. _a known bug in python-keystoneclient: https://bugs.launchpad.net/keystone/+bug/1004114 File Uploads ============ Horizon allows users to upload files via their web browser to other OpenStack services such as Glance and Swift. Files uploaded through this mechanism are first stored on the Horizon server before being forwarded on - files are not uploaded directly or streamed as Horizon receives them. As Horizon itself does not impose any restrictions on the size of file uploads, production deployments will want to consider configuring their server hosting the Horizon application to enforce such a limit to prevent large uploads exhausting system resources and disrupting services. Deployments using Apache2 can use the `LimitRequestBody directive`_ to achieve this. Uploads to the Glance image store service tend to be particularly large - in the order of hundreds of megabytes to multiple gigabytes. Deployments are able to disable local image uploads through Horizon by setting ``HORIZON_IMAGES_ALLOW_UPLOAD`` to ``False`` in your ``local_settings.py`` file. .. note:: This will not disable image creation altogether, as this setting does not affect images created by specifying an image location (URL) as the image source. .. _LimitRequestBody directive: http://httpd.apache.org/docs/2.2/mod/core.html#limitrequestbody Session Storage =============== Horizon uses `Django's sessions framework`_ for handling user session data; however that's not the end of the story. There are numerous session backends available, which are controlled through the ``SESSION_ENGINE`` setting in your ``local_settings.py`` file. What follows is a quick discussion of the pros and cons of each of the common options as they pertain to deploying Horizon specifically. .. _Django's sessions framework: https://docs.djangoproject.com/en/dev/topics/http/sessions/ Local Memory Cache ------------------ Enabled by:: SESSION_ENGINE = 'django.contrib.sessions.backends.cache' CACHES = { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache' } Local memory storage is the quickest and easiest session backend to set up, as it has no external dependencies whatsoever. However, it has two significant drawbacks: * No shared storage across processes or workers. * No persistence after a process terminates. The local memory backend is enabled as the default for Horizon solely because it has no dependencies. It is not recommended for production use, or even for serious development work. For better options, read on. Memcached --------- Enabled by:: SESSION_ENGINE = 'django.contrib.sessions.backends.cache' CACHES = { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache' 'LOCATION': 'my_memcached_host:11211', } External caching using an application such as memcached offers persistence and shared storage, and can be very useful for small-scale deployment and/or development. However, for distributed and high-availability scenarios memcached has inherent problems which are beyond the scope of this documentation. Memcached is an extremely fast and efficient cache backend for cases where it fits the deployment need. But it's not appropriate for all scenarios. Requirements: * Memcached service running and accessible. * Python memcached module installed. Database -------- Enabled by:: SESSION_ENGINE = 'django.core.cache.backends.db.DatabaseCache' DATABASES = { 'default': { # Database configuration here } } Database-backed sessions are scalable (using an appropriate database strategy), persistent, and can be made high-concurrency and highly-available. The downside to this approach is that database-backed sessions are one of the slower session storages, and incur a high overhead under heavy usage. Proper configuration of your database deployment can also be a substantial undertaking and is far beyond the scope of this documentation. Cached Database --------------- To mitigate the performance issues of database queries, you can also consider using Django's ``cached_db`` session backend which utilizes both your database and caching infrastructure to perform write-through caching and efficient retrieval. You can enable this hybrid setting by configuring both your database and cache as discussed above and then using:: SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" Cookies ------- ``signed_cookies`` is a session backend that is available to you which avoids server load and scaling problems. This backend stores session data in a cookie which is stored by the user's browser. The backend uses a cryptographic signing technique to ensure session data is not tampered with during transport (**this is not the same as encryption, session data is still readable by an attacker**). The pros of this session engine are that it doesn't require any additional dependencies or infrastructure overhead, and it scales indefinitely as long as the quantity of session data being stored fits into a normal cookie. The biggest downside is that it places session data into storage on the user's machine and transports it over the wire. It also limits the quantity of session data which can be stored. For a thorough discussion of the security implications of this session backend, please read the `Django documentation on cookie-based sessions`_. .. _Django documentation on cookie-based sessions: https://docs.djangoproject.com/en/dev/topics/http/sessions/#using-cookie-based-sessions Secure Site Recommendations --------------------------- When implementing Horizon for public usage, with the website served through HTTPS, it is recommended that the following settings are applied. To help protect the session cookies from `cross-site scripting`_, add the following to ``local_settings.py``:: CSRF_COOKIE_HTTPONLY = True SESSION_COOKIE_HTTPONLY = True Client-side JavaScript will not be able to access the cookie if this set to True. Note that the HTTPOnly is a flag included in Set-Cookie HTTP response header and is not honored consistently by all browsers. Additionally, adding the following flags to ``local_settings.py`` marks the cookies as secure, which ensures that the cookie is only sent under an HTTPS connection:: CSRF_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True You can also disable `browser autocompletion`_ for the authentication form by modifying the ``HORIZON_CONFIG`` dictionary in ``local_settings.py`` by adding the key ``password_autocomplete`` with the value ``off`` as shown here:: HORIZON_CONFIG = { ... 'password_autocomplete': 'off', } .. _cross-site scripting: https://www.owasp.org/index.php/HttpOnly .. _browser autocompletion: https://wiki.mozilla.org/The_autocomplete_attribute_and_web_documents_using_XHTML horizon-9.0.0/doc/source/topics/customizing.rst0000664000567000056710000005620012701407063023027 0ustar jenkinsjenkins00000000000000=================== Customizing Horizon =================== Themes ====== As of the Kilo release, styling for the OpenStack Dashboard can be altered through the use of a theme. A theme is a directory containing a ``_variables.scss`` file to override the color codes used throughout the SCSS and a ``_styles.scss`` file with additional styles to load after dashboard styles have loaded. As of the Mitaka release, Horizon can be configured to run with multiple themes available at run time. It uses a browser cookie to allow users to toggle between the configured themes. By default, Horizon is configured with the two standard themes available: 'default' and 'material'. To configure or alter the available themes, set ``AVAILABLE_THEMES`` in ``local_settings.py`` to a list of tuples, such that ``('name', 'label', 'path')`` ``name`` The key by which the theme value is stored within the cookie ``label`` The label shown in the theme toggle under the User Menu ``path`` The directory location for the theme. The path must be relative to the ``openstack_dashboard`` directory or an absolute path to an accessible location on the file system To use a custom theme, set ``AVAILABLE_THEMES`` in ``local_settings.py`` to a list of themes. If you wish to run in a mode similar to legacy Horizon, set ``AVAILABLE_THEMES`` with a single tuple, and the theme toggle will not be available at all through the application to allow user configuration themes. For example, a configuration with multiple themes:: AVAILABLE_THEMES = [ ('default', 'Default', 'themes/default'), ('material', 'Material', 'themes/material'), ] A configuration with a single theme:: AVAILABLE_THEMES = [ ('default', 'Default', 'themes/default'), ] Both the Dashboard custom variables and Bootstrap variables can be overridden. For a full list of the Dashboard SCSS variables that can be changed, see the variables file at ``openstack_dashboard/static/dashboard/scss/_variables.scss``. In order to build a custom theme, both ``_variables.scss`` and ``_styles.scss`` are required and ``_variables.scss`` must provide all the default Bootstrap variables. Inherit from an Existing Theme ------------------------------ Custom themes must implement all of the Bootstrap variables required by Horizon in ``_variables.scss`` and ``_styles.scss``. To make this easier, you can inherit the variables needed in the default theme and only override those that you need to customize. To inherit from the default theme, put this in your theme's ``_variables.scss``:: @import "/themes/default/variables"; Once you have made your changes you must re-generate the static files with ``./run_tests.py -m collectstatic``. By default, all of the themes configured by ``AVAILABLE_THEMES`` setting are collected by horizon during the `collectstatic` process. By default, the themes are collected into the dynamic `static/themes` directory, but this location can be customized via the ``local_settings.py`` variable: ``THEME_COLLECTION_DIR`` Once collected, any theme configured via ``AVAILABLE_THEMES`` is available to inherit from by importing its variables and styles from its collection directory. The following is an example of inheriting from the material theme:: @import "/themes/material/variables"; @import "/themes/material/styles"; Bootswatch ~~~~~~~~~~ Horizon packages the Bootswatch SCSS files for use with its ``material`` theme. Because of this, it is simple to use an existing Bootswatch theme as a base. This is due to the fact that Bootswatch is loaded as a 3rd party static asset, and therefore is automatically collected into the `static` directory in `/horizon/lib/`. The following is an example of how to inherit from Bootswatch's ``darkly`` theme:: @import "/horizon/lib/bootswatch/darkly/variables"; @import "/horizon/lib/bootswatch/darkly/bootswatch"; Organizing Your Theme Directory ------------------------------- A custom theme directory can be organized differently, depending on the level of customization that is desired, as it can include static files as well as Django templates. It can include special subdirectories that will be used differently: ``static``, ``templates`` and ``img``. The ``static`` Folder ~~~~~~~~~~~~~~~~~~~~~ If the theme folder contains a sub-folder called ``static``, then that sub folder will be used as the **static root of the theme**. I.e., Horizon will look in that sub-folder for the _variables.scss and _styles.scss files. The contents of this folder will also be served up at ``/static/custom``. The ``templates`` Folder ~~~~~~~~~~~~~~~~~~~~~~~~ If the theme folder contains a sub-folder ``templates``, then the path to that sub-folder will be prepended to the ``TEMPLATE_DIRS`` tuple to allow for theme specific template customizations. Using the ``templates`` Folder ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Any Django template that is used in Horizon can be overridden through a theme. This allows highly customized user experiences to exist within the scope of different themes. Any template that is overridden must adhere to the same directory structure that the extending template expects. For example, if you wish to customize the sidebar, Horizon expects the template to live at ``horizon/_sidebar.html``. You would need to duplicate that directory structure under your templates directory, such that your override would live at ``{ theme_path }/templates/horizon/_sidebar.html``. The ``img`` Folder ~~~~~~~~~~~~~~~~~~ If the static root of the theme folder contains an ``img`` directory, then all images that make use of the {% themable_asset %} templatetag can be overridden. These assets include logo.png, splash-logo.png and favicon.ico, however overriding the SVG/GIF assets used by Heat within the `dashboard/img` folder is not currently supported. Customizing the Logo -------------------- Simple ~~~~~~ If you wish to customize the logo that is used on the splash screen or in the top navigation bar, then you need to create an ``img`` directory under your theme's static root directory and place your custom ``logo.png`` or ``logo-splash.png`` within it. If you wish to override the ``logo.png`` using the previous method, and if the image used is larger than the height of the top navigation, then the image will be constrained to fit within the height of nav. You can customize the height of the top navigation bar by customizing the SCSS variable: ``$navbar-height``. If the image's height is smaller than the navbar height, then the image will retain its original resolution and size, and simply be centered vertically in the available space. Prior to the Kilo release the images files inside of Horizon needed to be replaced by your images files or the Horizon stylesheets needed to be altered to point to the location of your image. Advanced ~~~~~~~~ If you need to do more to customize the logo than simply replacing the existing PNG, then you can also override the _brand.html through a custom theme. To use this technique, simply add a ``templates/header/_brand.html`` to the root of your custom theme, and add markup directly to the file. For an example of how to do this, see ``openstack_dashboard/themes/material/templates/header/_brand.html``. The splash / login panel can also be customized by adding ``templates/auth/_splash.html``. See ``openstack_dashboard/themes/material/templates/auth/_splash.html`` for an example. Branding Horizon ================ As of the Liberty release, Horizon has begun to conform more strictly to Bootstrap standards in an effort to embrace more responsive web design as well as alleviate the future need to re-brand new functionality for every release. Supported Components -------------------- The following components, organized by release, are the only ones that make full use of the Bootstrap theme architecture. 8.0.0 (Liberty) ~~~~~~~~~~~~~~~ * `Top Navbar`_ * `Side Nav`_ * `Pie Charts`_ 9.0.0 (Mitaka) ~~~~~~~~~~~~~~ * Tables_ * `Bar Charts`_ * Login_ * Tabs_ * Alerts_ * Checkboxes_ Step 1 ------ The first step needed to create a custom branded theme for Horizon is to create a custom Bootstrap theme. There are several tools to aid in this. Some of the more useful ones include: - `Bootswatchr`_ - `Paintstrap`_ - `Bootstrap`_ .. note:: Bootstrap uses LESS by default, but we use SCSS. All of the above tools will provide the ``variables.less`` file, which will need to be converted to ``_variables.scss`` Top Navbar ---------- The top navbar in Horizon now uses a native Bootstrap ``navbar``. There are a number of variables that can be used to customize this element. Please see the **Navbar** section of your variables file for specifics on what can be set: any variables that use ``navbar-default``. It is important to also note that the navbar now uses native Bootstrap dropdowns, which are customizable with variables. Please see the **Dropdowns** section of your variables file. The top navbar is now responsive on smaller screens. When the window size hits your ``$screen-sm`` value, the topbar will compress into a design that is better suited for small screens. Side Nav -------- The side navigation component has been refactored to use the native Stacked Pills element from Bootstrap. See **Pills** section of your variables file for specific variables to customize. Charts ------ Pie Charts ~~~~~~~~~~ Pie Charts are SVG elements. SVG elements allow CSS customizations for only a basic element's look and feel (i.e. colors, size). Since there is no native element in Bootstrap specifically for pie charts, the look and feel of the charts are inheriting from other elements of the theme. Please see ``_pie_charts.scss`` for specifics. .. _Bar Charts: Bar Charts ~~~~~~~~~~ Bar Charts can be either a Bootstrap Progress Bar or an SVG element. Either implementation will use the Bootstrap Progress Bar styles. The SVG implementation will not make use of the customized Progress Bar height though, so it is recommended that Bootstrap Progress Bars are used whenever possible. Please see ``_bar_charts.scss`` for specifics on what can be customized for SVGs. See the **Progress bars** section of your variables file for specific variables to customize. Tables ------ The standard Django tables now make use of the native Bootstrap table markup. See **Tables** section of your variables file for variables to customize. The standard Bootstrap tables will be borderless by default. If you wish to add a border, like the ``default`` theme, see ``openstack_dashboard/themes/default/horizon/components/_tables.scss`` .. _Login: Login ----- Login Splash Page ~~~~~~~~~~~~~~~~~ The login splash page now uses a standard Bootstrap panel in its implementation. See the **Panels** section in your variables file to variables to easily customize. Modal Login ~~~~~~~~~~~ The modal login experience, as used when switching regions, uses a standard Bootstrap dialog. See the **Modals** section of your variables file for specific variables to customize. Tabs ---- The standard tabs make use of the native Bootstrap tab markup. See **Tabs** section of your variables file for variables to customize. Alerts ------ Alerts use the basic Bootstrap brand colors. See **Colors** section of your variables file for specifics. Checkboxes ---------- Horizon uses icon fonts to represent checkboxes. In order to customize this, you simply need to override the standard scss. For an example of this, see themes/material/static/horizon/components/_checkboxes.scss Bootswatch and Material Design ------------------------------ `Bootswatch`_ is a collection of free themes for Bootstrap and is now available for use in Horizon. In order to showcase what can be done to enhance an existing Bootstrap theme, Horizon now includes a secondary theme, roughly based on `Google's Material Design`_ called ``material``. Bootswatch's **Paper** is a simple Bootstrap implementation of Material Design and is used by ``material``. Bootswatch provides a number of other themes, that once Horizon is fully theme compliant, will allow easy toggling and customizations for darker or accessibility driven experiences. Development Tips ---------------- When developing a new theme for Horizon, it is required that the dynamically generated `static` directory be cleared after each change and the server restarted. This is not always ideal. If you wish to develop and not have to restart the server each time, it is recommended that you configure your development environment to not run in OFFLINE mode. Simply verify the following settings in your local_settings.py:: COMPRESS_OFFLINE = False COMPRESS_ENABLED = False Changing the Site Title ======================= The OpenStack Dashboard Site Title branding (i.e. "**OpenStack** Dashboard") can be overwritten by adding the attribute ``SITE_BRANDING`` to ``local_settings.py`` with the value being the desired name. The file ``local_settings.py`` can be found at the Horizon directory path of ``openstack_dashboard/local/local_settings.py``. Changing the Brand Link ======================= The logo also acts as a hyperlink. The default behavior is to redirect to ``horizon:user_home``. By adding the attribute ``SITE_BRANDING_LINK`` with the desired url target e.g., ``http://sample-company.com`` in ``local_settings.py``, the target of the hyperlink can be changed. Customizing the Footer ====================== It is possible to customize the global and login footers using a theme's template override. Simply add ``_footer.html`` for a global footer override or ``_login_footer.html`` for the login page's footer to your theme's template directory. Modifying Existing Dashboards and Panels ======================================== If you wish to alter dashboards or panels which are not part of your codebase, you can specify a custom python module which will be loaded after the entire Horizon site has been initialized, but prior to the URLconf construction. This allows for common site-customization requirements such as: * Registering or unregistering panels from an existing dashboard. * Changing the names of dashboards and panels. * Re-ordering panels within a dashboard or panel group. Default Horizon panels are loaded based upon files within the openstack_dashboard/enabled/ folder. These files are loaded based upon the filename order, with space left for more files to be added. There are some example files available within this folder, with the .example suffix added. Developers and deployers should strive to use this method of customization as much as possible, and support for this is given preference over more exotic methods such as monkey patching and overrides files. Horizon customization module (overrides) ======================================== Horizon has a global overrides mechanism available to perform customizations that are not yet customizable via configuration settings. This file can perform monkey patching and other forms of customization which are not possible via the enabled folder's customization method. This method of customization is meant to be available for deployers of Horizon, and use of this should be avoided by Horizon plugins at all cost. Plugins needing this level of monkey patching and flexibility should instead look for changing their __init__.py file and performing customizations through other means. To specify the python module containing your modifications, add the key ``customization_module`` to your ``HORIZON_CONFIG`` dictionary in ``local_settings.py``. The value should be a string containing the path to your module in dotted python path notation. Example:: HORIZON_CONFIG = { "customization_module": "my_project.overrides" } You can do essentially anything you like in the customization module. For example, you could change the name of a panel:: from django.utils.translation import ugettext_lazy as _ import horizon # Rename "User Settings" to "User Options" settings = horizon.get_dashboard("settings") user_panel = settings.get_panel("user") user_panel.name = _("User Options") Or get the instances panel:: projects_dashboard = horizon.get_dashboard("project") instances_panel = projects_dashboard.get_panel("instances") And limit access to users with the Keystone Admin role:: permissions = list(getattr(instances_panel, 'permissions', [])) permissions.append('openstack.roles.admin') instances_panel.permissions = tuple(permissions) Or just remove it entirely:: projects_dashboard.unregister(instances_panel.__class__) You cannot unregister a ``default_panel``. If you wish to remove a ``default_panel``, you need to make a different panel in the dashboard as a ``default_panel`` and then unregister the former. For example, if you wished to remove the ``overview_panel`` from the ``Project`` dashboard, you could do the following:: project = horizon.get_dashboard('project') project.default_panel = "instances" overview = project.get_panel('overview') project.unregister(overview.__class__) You can also override existing methods with your own versions:: # Disable Floating IPs from openstack_dashboard.dashboards.project.access_and_security import tabs from openstack_dashboard.dashboards.project.instances import tables NO = lambda *x: False tabs.FloatingIPsTab.allowed = NO tables.AssociateIP.allowed = NO tables.SimpleAssociateIP.allowed = NO tables.SimpleDisassociateIP.allowed = NO You could also customize what columns are displayed in an existing table, by redefining the ``columns`` attribute of its ``Meta`` class. This can be achieved in 3 steps: #. Extend the table that you wish to modify #. Redefine the ``columns`` attribute under the ``Meta`` class for this new table #. Modify the ``table_class`` attribute for the related view so that it points to the new table For example, if you wished to remove the Admin State column from the :class:`~openstack_dashboard.dashboards.admin.networks.tables.NetworksTable`, you could do the following:: from openstack_dashboard.dashboards.project.networks import tables from openstack_dashboard.dashboards.project.networks import views class MyNetworksTable(tables.NetworksTable): class Meta(tables.NetworksTable.Meta): columns = ('name', 'subnets', 'shared', 'status') views.IndexView.table_class = MyNetworksTable If you want to add a column you can override the parent table in a similar way, add the new column definition and then use the ``Meta`` ``columns`` attribute to control the column order as needed. .. NOTE:: ``my_project.overrides`` needs to be importable by the python process running Horizon. If your module is not installed as a system-wide python package, you can either make it installable (e.g., with a setup.py) or you can adjust the python path used by your WSGI server to include its location. Probably the easiest way is to add a ``python-path`` argument to the ``WSGIDaemonProcess`` line in Apache's Horizon config. Assuming your ``my_project`` module lives in ``/opt/python/my_project``, you'd make it look like the following:: WSGIDaemonProcess [... existing options ...] python-path=/opt/python Icons ===== Horizon uses font icons from Font Awesome. Please see `Font Awesome`_ for instructions on how to use icons in the code. To add icon to Table Action, use icon property. Example: class CreateSnapshot(tables.LinkAction): name = "snapshot" verbose_name = _("Create Snapshot") icon = "camera" Additionally, the site-wide default button classes can be configured by setting ``ACTION_CSS_CLASSES`` to a tuple of the classes you wish to appear on all action buttons in your ``local_settings.py`` file. Custom Stylesheets ================== It is possible to define custom stylesheets for your dashboards. Horizon's base template ``openstack_dashboard/templates/base.html`` defines multiple blocks that can be overridden. To define custom css files that apply only to a specific dashboard, create a base template in your dashboard's templates folder, which extends Horizon's base template e.g. ``openstack_dashboard/dashboards/my_custom_dashboard/ templates/my_custom_dashboard/base.html``. In this template, redefine ``block css``. (Don't forget to include ``_stylesheets.html`` which includes all Horizon's default stylesheets.):: {% extends 'base.html' %} {% block css %} {% include "_stylesheets.html" %} {% load compress %} {% compress css %} {% endcompress %} {% endblock %} The custom stylesheets then reside in the dashboard's own ``static`` folder ``openstack_dashboard/dashboards/my_custom_dashboard/static/ my_custom_dashboard/scss/my_custom_dashboard.scss``. All dashboard's templates have to inherit from dashboard's base.html:: {% extends 'my_custom_dashboard/base.html' %} ... Custom Javascript ================= Similarly to adding custom styling (see above), it is possible to include custom javascript files. All Horizon's javascript files are listed in the ``openstack_dashboard/ templates/horizon/_scripts.html`` partial template, which is included in Horizon's base template in ``block js``. To add custom javascript files, create an ``_scripts.html`` partial template in your dashboard ``openstack_dashboard/dashboards/my_custom_dashboard/ templates/my_custom_dashboard/_scripts.html`` which extends ``horizon/_scripts.html``. In this template override the ``block custom_js_files`` including your custom javascript files:: {% extends 'horizon/_scripts.html' %} {% block custom_js_files %} {% endblock %} In your dashboard's own base template ``openstack_dashboard/dashboards/ my_custom_dashboard/templates/my_custom_dashboard/base.html`` override ``block js`` with inclusion of dashboard's own ``_scripts.html``:: {% block js %} {% include "my_custom_dashboard/_scripts.html" %} {% endblock %} The result is a single compressed js file consisting both Horizon and dashboard's custom scripts. Additionally, some marketing and analytics scripts require you to place them within the page's tag. To do this, place them within the ``horizon/_custom_head_js.html`` file. Similar to the ``_scripts.html`` file mentioned above, you may link to an existing file:: or you can paste your script directly in the file, being sure to use appropriate tags:: Customizing Meta Attributes =========================== To add custom metadata attributes to your project's base template, include them in the ``horizon/_custom_meta.html`` file. The contents of this file will be inserted into the page's just after the default Horizon meta tags. .. _Bootswatch: http://bootswatch.com .. _Bootswatchr: http://bootswatchr.com/create#! .. _Paintstrap: http://paintstrap.com .. _Bootstrap: http://getbootstrap.com/customize/ .. _Google's Material Design: https://www.google.com/design/spec/material-design/introduction.html .. _Font Awesome: https://fortawesome.github.io/Font-Awesome/ horizon-9.0.0/doc/source/topics/workflows.rst0000664000567000056710000001303612701407063022511 0ustar jenkinsjenkins00000000000000====================== Workflows Topic Guide ====================== One of the most challenging aspects of building a compelling user experience is crafting complex multi-part workflows. Horizon's ``workflows`` module aims to bring that capability within everyday reach. .. seealso:: For detailed API information refer to the :doc:`Workflows Reference Guide `. Workflows ========= Workflows are complex forms with tabs, each workflow must consist of classes extending the :class:`~horizon.workflows.Workflow`, :class:`~horizon.workflows.Step` and :class:`~horizon.workflows.Action` Complex example of a workflow ------------------------------ The following is a complex example of how data is exchanged between urls, views, workflows and templates: #. In ``urls.py``, we have the named parameter. E.g. ``resource_class_id``. :: RESOURCE_CLASS = r'^(?P[^/]+)/%s$' urlpatterns = patterns( '', url(RESOURCE_CLASS % 'update', UpdateView.as_view(), name='update')) #. In ``views.py``, we pass data to the template and to the action(form) (action can also pass data to the ``get_context_data`` method and to the template). :: class UpdateView(workflows.WorkflowView): workflow_class = UpdateResourceClass def get_context_data(self, **kwargs): context = super(UpdateView, self).get_context_data(**kwargs) # Data from URL are always in self.kwargs, here we pass the data # to the template. context["resource_class_id"] = self.kwargs['resource_class_id'] # Data contributed by Workflow's Steps are in the # context['workflow'].context list. We can use that in the # template too. return context def _get_object(self, *args, **kwargs): # Data from URL are always in self.kwargs, we can use them here # to load our object of interest. resource_class_id = self.kwargs['resource_class_id'] # Code omitted, this method should return some object obtained # from API. def get_initial(self): resource_class = self._get_object() # This data will be available in the Action's methods and # Workflow's handle method. # But only if the steps will depend on them. return {'resource_class_id': resource_class.id, 'name': resource_class.name, 'service_type': resource_class.service_type} #. In ``workflows.py`` we process the data, it is just more complex django form. :: class ResourcesAction(workflows.Action): # The name field will be automatically available in all action's # methods. # If we want this field to be used in the another Step or Workflow, # it has to be contributed by this step, then depend on in another # step. name = forms.CharField(max_length=255, label=_("Testing Name"), help_text="", required=True) def handle(self, request, data): pass # If we want to use some data from the URL, the Action's step # has to depend on them. It's then available in # self.initial['resource_class_id'] or data['resource_class_id']. # In other words, resource_class_id has to be passed by view's # get_initial and listed in step's depends_on list. # We can also use here the data from the other steps. If we want # the data from the other step, the step needs to contribute the # data and the steps needs to be ordered properly. class UpdateResources(workflows.Step): # This passes data from Workflow context to action methods # (handle, clean). Workflow context consists of URL data and data # contributed by other steps. depends_on = ("resource_class_id",) # By contributing, the data on these indexes will become available to # Workflow and to other Steps (if they will depend on them). Notice, # that the resources_object_ids key has to be manually added in # contribute method first. contributes = ("resources_object_ids", "name") def contribute(self, data, context): # We can obtain the http request from workflow. request = self.workflow.request if data: # Only fields defined in Action are automatically # available for contribution. If we want to contribute # something else, We need to override the contribute method # and manually add it to the dictionary. context["resources_object_ids"] =\ request.POST.getlist("resources_object_ids") # We have to merge new context with the passed data or let # the superclass do this. context.update(data) return context class UpdateResourceClass(workflows.Workflow): default_steps = (UpdateResources,) def handle(self, request, data): pass # This method is called as last (after all Action's handle # methods). All data that are listed in step's 'contributes=' # and 'depends_on=' are available here. # It can be easier to have the saving logic only here if steps # are heavily connected or complex. # data["resources_object_ids"], data["name"] and # data["resources_class_id"] are available here. horizon-9.0.0/doc/source/topics/javascript_testing.rst0000664000567000056710000002135412701407071024360 0ustar jenkinsjenkins00000000000000================== JavaScript Testing ================== There are multiple components in our JavaScript testing framework: * `Jasmine`_ is our testing framework, so this defines the syntax and file structure we use to test our JavaScript. * `Karma`_ is our test runner. Amongst other things, this lets us run the tests against multiple browsers and generate test coverage reports. Alternatively, tests can be run inside the browser with the Jasmine spec runner. * `PhantomJS`_ provides a headless WebKit (the browser engine). This gives us native support for many web features without relying on specific browsers being installed. * `ESLint`_ is a pluggable code linting utilty. This will catch small errors and inconsistencies in your JS, which may lead to bigger issues later on. See :ref:`js_code_style` for more detail. Jasmine uses specs (``.spec.js``) which are kept with the JavaScript files that they are testing. See the :ref:`js_file_structure` section or the `Examples`_ below for more detail on this. .. _Jasmine: https://jasmine.github.io/2.3/introduction.html .. _Karma: https://karma-runner.github.io/ .. _PhantomJS: http://phantomjs.org/ .. _ESLint: http://eslint.org/ Running Tests ============= Tests can be run in two ways: 1. Open /jasmine in a browser. The development server can be run with ``./run_tests.sh --runserver`` from the horizon root directory. 2. ``npm run test`` from the horizon root directory. This runs Karma, so it will run all the tests against PhantomJS and generate coverage reports. The code linting job can be run with ``npm run lint``. Coverage Reports ---------------- Our Karma setup includes a plugin to generate test coverage reports. When developing, be sure to check the coverage reports on the master branch and compare your development branch; this will help identify missing tests. To generate coverage reports, run ``npm run test``. The coverage reports can be found at ``horizon/coverage-karma/`` (framework tests) and ``openstack_dashboard/coverage-karma/`` (dashboard tests). Load ``/index.html`` in a browser to view the reports. Writing Tests ============= Jasmine uses suites and specs: * Suites begin with a call to ``describe``, which takes two parameters; a string and a function. The string is a name or title for the spec suite, whilst the function is a block that implements the suite. * Specs begin with a call to ``it``, which also takes a string and a function as parameters. The string is a name or title, whilst the function is a block with one or more expectations (``expect``) that test the state of the code. An expectation in Jasmine is an assertion that is either true or false; every expectation in a spec must be true for the spec to pass. ``.spec.js`` files can be handled manually or automatically. To use the automatic file discovery add:: AUTO_DISCOVER_STATIC_FILES = True to your enabled file. JS code for testing should use the extensions ``.mock.js`` and ``.spec.js``. You can read more about the functionality in the :ref:`auto_discover_static_files` section of the settings documentation. To manually add specs, add the following array and relevant file paths to your enabled file: :: ADD_JS_SPEC_FILES = [ ... 'path_to/my_angular_code.spec.js', ... ] Examples ======== .. Note:: The code below is just for example purposes, and may not be current in horizon. Ellipses (...) are used to represent code that has been removed for the sake of brevity. Example 1 - A reusable component in the **horizon** directory ------------------------------------------------------------- File tree: :: horizon/static/framework/widgets/modal ├── modal.controller.js ├── modal.module.js ├── modal.service.js └── modal.spec.js Lines added to ``horizon/test/jasmine/jasmine_tests.py``: :: class ServicesTests(test.JasmineTests): sources = [ ... 'framework/widgets/modal/modal.module.js', 'framework/widgets/modal/modal.controller.js', 'framework/widgets/modal/modal.service.js', ... ] specs = [ ... 'framework/widgets/modal/modal.spec.js', ... ] ``modal.spec.js``: :: ... (function() { "use strict"; describe('horizon.framework.widgets.modal module', function() { beforeEach(module('horizon.framework')); describe('simpleModalCtrl', function() { var scope; var modalInstance; var context; var ctrl; beforeEach(inject(function($controller) { scope = {}; modalInstance = { close: function() {}, dismiss: function() {} }; context = { what: 'is it' }; ctrl = $controller('simpleModalCtrl', { $scope: scope, $modalInstance: modalInstance, context: context }); })); it('establishes a controller', function() { expect(ctrl).toBeDefined(); }); it('sets context on the scope', function() { expect(scope.context).toBeDefined(); expect(scope.context).toEqual({ what: 'is it' }); }); it('sets action functions', function() { expect(scope.submit).toBeDefined(); expect(scope.cancel).toBeDefined(); }); it('makes submit close the modal instance', function() { expect(scope.submit).toBeDefined(); spyOn(modalInstance, 'close'); scope.submit(); expect(modalInstance.close.calls.count()).toBe(1); }); it('makes cancel close the modal instance', function() { expect(scope.cancel).toBeDefined(); spyOn(modalInstance, 'dismiss'); scope.cancel(); expect(modalInstance.dismiss).toHaveBeenCalledWith('cancel'); }); }); ... }); })(); Example 2 - Panel-specific code in the **openstack_dashboard** directory ------------------------------------------------------------------------ File tree: :: openstack_dashboard/static/dashboard/launch-instance/network/ ├── network.help.html ├── network.html ├── network.js ├── network.scss └── network.spec.js Lines added to ``openstack_dashboard/enabled/_10_project.py``: :: LAUNCH_INST = 'dashboard/launch-instance/' ADD_JS_FILES = [ ... LAUNCH_INST + 'network/network.js', ... ] ADD_JS_SPEC_FILES = [ ... LAUNCH_INST + 'network/network.spec.js', ... ] ``network.spec.js``: :: ... (function(){ 'use strict'; describe('Launch Instance Network Step', function() { describe('LaunchInstanceNetworkCtrl', function() { var scope; var ctrl; beforeEach(module('horizon.dashboard.project.workflow.launch-instance')); beforeEach(inject(function($controller) { scope = { model: { newInstanceSpec: {networks: ['net-a']}, networks: ['net-a', 'net-b'] } }; ctrl = $controller('LaunchInstanceNetworkCtrl', {$scope:scope}); })); it('has correct network statuses', function() { expect(ctrl.networkStatuses).toBeDefined(); expect(ctrl.networkStatuses.ACTIVE).toBeDefined(); expect(ctrl.networkStatuses.DOWN).toBeDefined(); expect(Object.keys(ctrl.networkStatuses).length).toBe(2); }); it('has correct network admin states', function() { expect(ctrl.networkAdminStates).toBeDefined(); expect(ctrl.networkAdminStates.UP).toBeDefined(); expect(ctrl.networkAdminStates.DOWN).toBeDefined(); expect(Object.keys(ctrl.networkStatuses).length).toBe(2); }); it('defines a multiple-allocation table', function() { expect(ctrl.tableLimits).toBeDefined(); expect(ctrl.tableLimits.maxAllocation).toBe(-1); }); it('contains its own labels', function() { expect(ctrl.label).toBeDefined(); expect(Object.keys(ctrl.label).length).toBeGreaterThan(0); }); it('contains help text for the table', function() { expect(ctrl.tableHelpText).toBeDefined(); expect(ctrl.tableHelpText.allocHelpText).toBeDefined(); expect(ctrl.tableHelpText.availHelpText).toBeDefined(); }); it('uses scope to set table data', function() { expect(ctrl.tableDataMulti).toBeDefined(); expect(ctrl.tableDataMulti.available).toEqual(['net-a', 'net-b']); expect(ctrl.tableDataMulti.allocated).toEqual(['net-a']); expect(ctrl.tableDataMulti.displayedAllocated).toEqual([]); expect(ctrl.tableDataMulti.displayedAvailable).toEqual([]); }); }); ... }); })(); horizon-9.0.0/doc/source/topics/packaging.rst0000664000567000056710000002457212701407063022407 0ustar jenkinsjenkins00000000000000Packaging Software ================== Software packages ----------------- This section describes some general things that a developer should know about packaging software. This content is mostly derived from best practices. A developer building a package is comparable to an engineer building a car with only a manual and very few tools. If the engineer needs a specific tool to build the car, he must create the tool, too. As a developer, if you are going to add a library named “fooâ€, the package must adhere to the following standards: - Be a free package created with free software. - Include all tools that are required to build the package. - Have an active and responsive upstream to maintain the package. - Adhere to Filesystem Hierarchy Standards (FHS). A specific file system layout is not required. Embedded copies not allowed --------------------------- Imagine if all packages had a local copy of jQuery. If a security hole is discovered in jQuery, we must write more than 90 patches in Debian, one for each package that includes a copy. This is simply not practical. Therefore, it is unacceptable for Horizon to copy code from other repositories when creating a package. Copying code from another repository tends to create a fork, diverging from the upstream code. The fork includes code that is not being maintained, so if a bug is discovered in the original upstream, it cannot easily be fixed by updating a single package. Another reason to avoid copying a library into Horizon source code is that it might create conflicting licenses. Distributing sources with conflicting licenses in one tarball revokes rights in best case. In the worst case, you could be held legally responsible. Free software ------------- Red Hat, Debian, and SUSE distributions are made only of free software (free as in Libre, or free speech). The software that we include in our repository is free. The tools are also free, and available in the distribution. Because package maintainers care about the quality of the packages we upload, we run tests that are available from upstream repositories. This also qualifies test requirements as build requirements. The same rules apply for building the software as for the software itself. Special build requirements that are not included in the overall distribution are not allowed. An example of historically limiting, non-free software is Selenium. For a long time, Selenium was only available from the non-free repositories of Debian. The reason was that upstream included some .xpi binaries. These .xpi included some Windows .dll and Linux .so files. Because they could not be rebuilt from the source, all of python-selenium was declared non-free. If we made Horizon build-depends on python-selenium, this would mean Horizon wouldn't be in Debian main anymore (contrib and non-free are *not* considered part of Debian). Recently, the package maintainer of python-selenium decided to remove the .xpi files from python-selenium, and upload it to Debian Experimental (this time, in main, not in non-free). If at some point it is possible for Horizon to use python-selenium (without the non-free .xpi files), then we could run Selenium tests at package build time. Running unit tests at build time -------------------------------- The build environment inside a distribution is not exactly the same as the one in the OpenStack gate. For example, versions of a given library can be slightly different from the one in the gate. We want to detect when problematic differences exist so that we can fix them. Whenever possible, try to make the lives of the package maintainer easier, and allow them (or help them) to run unit tests. Minified JavaScript policy -------------------------- In free software distributions that actively maintain OpenStack packages (such as RDO, Debian, and Ubuntu), minified JavaScript is considered non-free. This means that minified JavaScript should *not* be present in upstream source code. At the very least, a non-minified version should be present next to the minified version. Also, be aware of potential security issues with minifiers. This `blog post`_ explains it very well. .. _`blog post`: https://zyan.scripts.mit.edu/blog/backdooring-js/ Component version ----------------- Be careful about the version of all the components you use in your application. Since it is not acceptable to embed a given component within Horizon, we must use what is in the distribution, including all fonts, JavaScript, etc. This is where it becomes a bit tricky. In most distributions, it is not acceptable to have multiple versions of the same piece of software. In Red Hat systems, it is technically possible to install 2 versions of one library at the same time, but a few restrictions apply, especially for usage. However, package maintainers try to avoid multiple versions as much as possible. For package dependency resolution, it might be necessary to provide packages for depending packages as well. For example, if you had Django-1.4 and Django-1.8 in the same release, you must provide Horizon built for Django-1.4 and another package providing Horizon built for Django-1.8. This is a large effort and needs to be evaluated carefully. In Debian, it is generally forbidden to have multiple versions of the same library in the same Debian release. Very few exceptions exist. Component versioning has consequences for an upstream author willing to integrate their software in a downstream distribution. The best situation is when it is possible to support whatever version is currently available in the target distributions, up to the latest version upstream. Declaring lower and upper bounds within your requirements.txt does not solve the issue. It allows all the tests to pass on gate because they are run against a narrow set of versions in requirements.txt. The downstream distribution might still have some dependencies with versions outside of the range that is specified in requirements.txt. These dependencies may lead to failures that are not caught in the OpenStack gate. At times it might not be possible to support all versions of a library. It might be too much work, or it might be very hard to test in the gate. In this case, it is best to use whatever is available inside the target distributions. For example, Horizon currently supports jQuery >= 1.7.2, as this is what is currently available in Debian Jessie and Ubuntu Trusty (the last LTS). You can search in a distribution for a piece of software foo using a command like ``dnf search foo``, or ``zypper se -s foo``. ``dnf info foo`` returns more detailed information about the package. Filesystem Hierarchy Standards ------------------------------ Every distribution must comply with the Filesystem Hierarchy Standards (FHS). The FHS defines a set of rules that we *must* follow as package maintainers. Some of the most important ones are: - /usr is considered read only. Software must not write in /usr at runtime. However, it is fine for a package post-installation script to write in /usr. When this rule was not followed, distributions had to write many tricks to convince Horizon to write in /var/lib only. For example, distributions wrote symlinks to /var/lib/openstack-dashboard, or patched the default local_settings.py to write the SECRET_KEY in /var. - Configuration must always be in /etc, no matter what. When this rule was not followed, package maintainers had to place symlinks to /etc/openstack-dashboard/local_settings in Red Hat based distributions instead of using directly /usr/share/openstack-dashboard/openstack_dashboard/local/local_settings.py which Horizon expects. In Debian,the configuration file is named /etc/openstack-dashboard/local_settings.py. Packaging Horizon ================= Why we use XStatic ------------------ XStatic provides the following features that are are not currently available by default with systems like NPM and Grunt: - Dependency checks: XStatic checks that dependencies, such as fonts and JavaScript libs, are available in downstream distributions. - Reusable components across projects: The XStatic system ensures components are reusable by other packages, like Fuel. - System-wide registry of static content: XStatic brings a system-wide registry of components, so that it is easy to check if one is missing. For example, it can detect if there is no egg-info, or a broken package dependency exists. - No embedded content: The XStatic system helps us avoid embedding files that are already available in the distribution, for example, libjs-* or fonts-* packages. It even provides a compatibility layer for distributions. Not every distribution places static files in the same position in the file system. If you are packaging an XStatic package for your distribution, make sure that you are using the static files provided by that specific distribution. Having put together an XStatic package is *no* guarantee to get it into a distribution. XStatic provides only the abstraction layer to use distribution provided static files. - Package build systems are disconnected from the outside network (for several reasons). Other packaging systems download dependencies directly from the internet without verifying that the downloaded file is intact, matches a provided checksum, etc. With these other systems, there is no way to provide a mirror, a proxy or a cache, making builds even more unstable when minor networking issues are encountered. The previous features are critical requirements of the Horizon packaging system. Any new system *must* keep these features. Although XStatic may mean a few additional steps from individual developers, those steps help maintain consistency and prevent errors across the project. Packaging Horizon for distributions ----------------------------------- Horizon is a Python module. Preferably, it is installed at the default location for python. In Fedora and openSUSE, this is /usr/lib/python2.7/site-packages/horizon, and in Debian/Ubuntu it is /usr/lib/python2.7/dist-packages/horizon. Configuration files should reside under /etc/openstack-dashboard. Policy files should be created and modified there as well. It is expected that ``manage.py collectstatic`` will be run during package build. This is the `recommended way`_ for Django applications. Depending on configuration, it might be required to ``manage.py compress`` during package build, too. .. _`recommended way`: https://docs.djangoproject.com/en/1.8/howto/static-files/deployment/ horizon-9.0.0/doc/source/topics/table_actions.rst0000664000567000056710000002427112701407063023266 0ustar jenkinsjenkins00000000000000============================================ Tutorial: Adding a complex action to a table ============================================ This tutorial covers how to add a more complex action to a table, one that requires an action and form definitions, as well as changes to the view, urls, and table. This tutorial assumes you have already completed :doc:`Building a Dashboard using Horizon `. If not, please do so now as we will be modifying the files created there. This action will create a snapshot of the instance. When the action is taken, it will display a form that will allow the user to enter a snapshot name, and will create that snapshot when the form is closed using the ``Create snapshot`` button. Defining the view ================= To define the view, we must create a view class, along with the template (``HTML``) file and the form class for that view. The template file ----------------- The template file contains the HTML that will be used to show the view. Create a ``create_snapshot.html`` file under the ``mypanel/templates/mypanel`` directory and add the following code:: {% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Create Snapshot" %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=_("Create a Snapshot") %} {% endblock page_header %} {% block main %} {% include 'mydashboard/mypanel/_create_snapshot.html' %} {% endblock %} As you can see, the main body will be defined in ``_create_snapshot.html``, so we must also create that file under the ``mypanel/templates/mypanel`` directory. It should contain the following code:: {% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Snapshots preserve the disk state of a running instance." %}

{% endblock %} The form -------- Horizon provides a :class:`~horizon.forms.base.SelfHandlingForm` class which simplifies some of the details involved in creating a form. Our form will derive from this class, adding a character field to allow the user to specify a name for the snapshot, and handling the successful closure of the form by calling the nova api to create the snapshot. Create the ``forms.py`` file under the ``mypanel`` directory and add the following:: from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from openstack_dashboard import api class CreateSnapshot(forms.SelfHandlingForm): instance_id = forms.CharField(label=_("Instance ID"), widget=forms.HiddenInput(), required=False) name = forms.CharField(max_length=255, label=_("Snapshot Name")) def handle(self, request, data): try: snapshot = api.nova.snapshot_create(request, data['instance_id'], data['name']) return snapshot except Exception: exceptions.handle(request, _('Unable to create snapshot.')) The view -------- Now, the view will tie together the template and the form. Horizon provides a :class:`~horizon.forms.views.ModalFormView` class which simplifies the creation of a view that will contain a modal form. Open the ``views.py`` file under the ``mypanel`` directory and add the code for the CreateSnapshotView and the necessary imports. The complete file should now look something like this:: from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse_lazy from django.utils.translation import ugettext_lazy as _ from horizon import tabs from horizon import exceptions from horizon import forms from horizon.utils import memoized from openstack_dashboard import api from openstack_dashboard.dashboards.mydashboard.mypanel \ import forms as project_forms from openstack_dashboard.dashboards.mydashboard.mypanel \ import tabs as mydashboard_tabs class IndexView(tabs.TabbedTableView): tab_group_class = mydashboard_tabs.MypanelTabs # A very simple class-based view... template_name = 'mydashboard/mypanel/index.html' def get_data(self, request, context, *args, **kwargs): # Add data to the context here... return context class CreateSnapshotView(forms.ModalFormView): form_class = project_forms.CreateSnapshot template_name = 'mydashboard/mypanel/create_snapshot.html' success_url = reverse_lazy("horizon:project:images:index") modal_id = "create_snapshot_modal" modal_header = _("Create Snapshot") submit_label = _("Create Snapshot") submit_url = "horizon:mydashboard:mypanel:create_snapshot" @memoized.memoized_method def get_object(self): try: return api.nova.server_get(self.request, self.kwargs["instance_id"]) except Exception: exceptions.handle(self.request, _("Unable to retrieve instance.")) def get_initial(self): return {"instance_id": self.kwargs["instance_id"]} def get_context_data(self, **kwargs): context = super(CreateSnapshotView, self).get_context_data(**kwargs) instance_id = self.kwargs['instance_id'] context['instance_id'] = instance_id context['instance'] = self.get_object() context['submit_url'] = reverse(self.submit_url, args=[instance_id]) return context Adding the url ============== We must add the url for our new view. Open the ``urls.py`` file under the ``mypanel`` directory and add the following as a new url pattern:: url(r'^(?P[^/]+)/create_snapshot/$', views.CreateSnapshotView.as_view(), name='create_snapshot'), The complete ``urls.py`` file should look like this:: from django.conf.urls import patterns from django.conf.urls import url from openstack_dashboard.dashboards.mydashboard.mypanel import views urlpatterns = patterns('', url(r'^$', views.IndexView.as_view(), name='index'), url(r'^(?P[^/]+)/create_snapshot/$', views.CreateSnapshotView.as_view(), name='create_snapshot'), ) Define the action ================= Horizon provides a :class:`~horizon.tables.LinkAction` class which simplifies adding an action which can be used to display another view. We will add a link action to the table that will be accessible from each row in the table. The action will use the view defined above to create a snapshot of the instance represented by the row in the table. To do this, we must edit the ``tables.py`` file under the ``mypanel`` directory and add the following:: def is_deleting(instance): task_state = getattr(instance, "OS-EXT-STS:task_state", None) if not task_state: return False return task_state.lower() == "deleting" class CreateSnapshotAction(tables.LinkAction): name = "snapshot" verbose_name = _("Create Snapshot") url = "horizon:mydashboard:mypanel:create_snapshot" classes = ("ajax-modal",) icon = "camera" # This action should be disabled if the instance # is not active, or the instance is being deleted def allowed(self, request, instance=None): return instance.status in ("ACTIVE") \ and not is_deleting(instance) We must also add our new action as a row action for the table:: row_actions = (CreateSnapshotAction,) The complete ``tables.py`` file should look like this:: from django.utils.translation import ugettext_lazy as _ from horizon import tables def is_deleting(instance): task_state = getattr(instance, "OS-EXT-STS:task_state", None) if not task_state: return False return task_state.lower() == "deleting" class CreateSnapshotAction(tables.LinkAction): name = "snapshot" verbose_name = _("Create Snapshot") url = "horizon:mydashboard:mypanel:create_snapshot" classes = ("ajax-modal",) icon = "camera" def allowed(self, request, instance=None): return instance.status in ("ACTIVE") \ and not is_deleting(instance) class MyFilterAction(tables.FilterAction): name = "myfilter" class InstancesTable(tables.DataTable): name = tables.Column("name", verbose_name=_("Name")) status = tables.Column("status", verbose_name=_("Status")) zone = tables.Column('availability_zone', verbose_name=_("Availability Zone")) image_name = tables.Column('image_name', verbose_name=_("Image Name")) class Meta: name = "instances" verbose_name = _("Instances") table_actions = (MyFilterAction,) row_actions = (CreateSnapshotAction,) Run and check the dashboard =========================== We must once again run horizon to verify our dashboard is working:: ./run_tests.sh --runserver 0.0.0.0:8877 Go to ``http://:8877`` using a browser. After login as an admin, display ``My Panel`` to see the ``Instances`` table. For every ``ACTIVE`` instance in the table, there will be a ``Create Snapshot`` action on the row. Click on ``Create Snapshot``, enter a snapshot name in the form that is shown, then click to close the form. The ``Project Images`` view should be shown with the new snapshot added to the table. Conclusion ========== What you've learned here is the fundamentals of how to add a table action that requires a form for data entry. This can easily be expanded from creating a snapshot to other API calls that require more complex forms to gather the necessary information. If you have feedback on how this tutorial could be improved, please feel free to submit a bug against ``Horizon`` in `launchpad`_. .. _launchpad: https://bugs.launchpad.net/horizon horizon-9.0.0/doc/source/plugin_registry.rst0000664000567000056710000001371212701407063022402 0ustar jenkinsjenkins00000000000000======================= Horizon Plugin Registry ======================= .. Note:: Currently, Horizon plugins are responsible for their own compatibility. Check the individual repos for information on support. +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Plugin |URL |Launchpad | +=======================+=====================================================+============================================+ |Astara Dashboard |https://github.com/openstack/astara-horizon |https://launchpad.net/astara | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |App Catalog UI |https://github.com/openstack/app-catalog-ui |https://launchpad.net/app-catalog | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Cerberus Dashboard |https://github.com/openstack/cerberus-dashboard |https://launchpad.net/cerberus | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Cisco UI |http://github.com/openstack/horizon-cisco-ui |https://launchpad.net/horizon-cisco-ui | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Cloudkitty Dashboard |https://github.com/openstack/cloudkitty-dashboard |https://launchpad.net/cloudkitty | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Cue Dashboard |https://github.com/openstack/cue-dashboard |https://launchpad.net/cue-dashboard | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Designate Dashboard |https://github.com/openstack/designate-dashboard |https://launchpad.net/designate | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Group Based Policy UI |https://github.com/openstack/group-based-policy-ui |https://launchpad.net/group-based-policy-ui | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Freezer Web UI |https://github.com/openstack/freezer-web-ui |https://launchpad.net/freezer | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Magnum UI |http://github.com/openstack/magnum-ui |https://launchpad.net/magnum-ui | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Manila UI |http://github.com/openstack/manila-ui |https://launchpad.net/manila-ui | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Mistral Dashboard |https://github.com/openstack/mistral-dashboard |https://launchpad.net/mistral | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Monasca UI |https://github.com/openstack/monasca-ui |https://launchpad.net/monasca | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Murano Dashboard |http://github.com/openstack/murano-dashboard |http://launchpad.net/murano | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Neutron LBaaS Dashboard|https://github.com/openstack/neutron-lbaas-dashboard |http://launchpad.net/neutron | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Sahara Dashboard |https://github.com/openstack/sahara-dashboard |https://launchpad.net/sahara | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Senlin Dashboard |https://github.com/openstack/senlin-dashboard |http://launchpad.net/senlin-dashboard | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Solum Dashboard |https://github.com/openstack/solum-dashboard |https://launchpad.net/solum | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Sticks Dashboard |https://github.com/openstack/sticks-dashboard |https://wiki.openstack.org/wiki/Sticks | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Tacker UI |https://github.com/openstack/tacker-horizon |https://launchpad.net/tacker | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Trove Dashboard |https://github.com/openstack/trove-dashboard |https://launchpad.net/trove | +-----------------------+-----------------------------------------------------+--------------------------------------------+ |Tuskar UI |http://github.com/openstack/tuskar-ui |https://launchpad.net/tuskar-ui | +-----------------------+-----------------------------------------------------+--------------------------------------------+ horizon-9.0.0/doc/source/testing.rst0000664000567000056710000000434312701407071020630 0ustar jenkinsjenkins00000000000000======================= Horizon's tests and you ======================= How to run the tests ==================== Because Horizon is composed of both the ``horizon`` app and the ``openstack_dashboard`` reference project, there are in fact two sets of unit tests. While they can be run individually without problem, there is an easier way: Included at the root of the repository is the ``run_tests.sh`` script which invokes both sets of tests, and optionally generates analyses on both components in the process. This script is what Jenkins uses to verify the stability of the project, so you should make sure you run it and it passes before you submit any pull requests/patches. To run the tests:: $ ./run_tests.sh It's also possible to :doc:`run a subset of unit tests`. .. seealso:: :doc:`ref/run_tests` Full reference for the ``run_tests.sh`` script. By default running the Selenium tests will open your Firefox browser (you have to install it first, else an error is raised), and you will be able to see the tests actions. If you want to run the suite headless, without being able to see them (as they are ran on Jenkins), you can run the tests: $ ./run_tests.sh --with-selenium --selenium-headless Selenium will use a virtual display in this case, instead of your own. In order to run the tests this way you have to install the dependency `xvfb`, like this: $ sudo apt-get install xvfb for a Debian OS flavour, or for Fedora/Red Hat flavours: $ sudo yum install xorg-x11-server-Xvfb If you can't run a virtual display, or would prefer not to, you can use the PhantomJS web driver instead: $ ./run_tests.sh --with-selenium --selenium-phantomjs If you need to install PhantomJS, you may do so with `npm` like this: $ npm -g install phantomjs Writing tests ============= Horizon uses Django's unit test machinery (which extends Python's ``unittest2`` library) as the core of its test suite. As such, all tests for the Python code should be written as unit tests. No doctests please. In general new code without unit tests will not be accepted, and every bugfix *must* include a regression test. For a much more in-depth discussion of testing, see the :doc:`testing topic guide `. horizon-9.0.0/doc/source/conf.py0000664000567000056710000003513712701407071017725 0ustar jenkinsjenkins00000000000000# 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. # # Horizon documentation build configuration file, created by # sphinx-quickstart on Thu Oct 27 11:38:59 2011. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. from __future__ import print_function import django import os import sys BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) sys.path.insert(0, ROOT) # This is required for ReadTheDocs.org, but isn't a bad idea anyway. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openstack_dashboard.settings') import horizon.version django.setup() def write_autodoc_index(): def find_autodoc_modules(module_name, sourcedir): """returns a list of modules in the SOURCE directory.""" modlist = [] os.chdir(os.path.join(sourcedir, module_name)) print("SEARCHING %s" % sourcedir) for root, dirs, files in os.walk("."): for filename in files: if filename == 'tests.py': continue if filename.endswith(".py"): # remove the pieces of the root elements = root.split(os.path.sep) # replace the leading "." with the module name elements[0] = module_name # and get the base module name base, extension = os.path.splitext(filename) if not (base == "__init__"): elements.append(base) result = ".".join(elements) # print result modlist.append(result) return modlist RSTDIR = os.path.abspath(os.path.join(BASE_DIR, "sourcecode")) SRCS = [('horizon', ROOT), ('openstack_dashboard', ROOT)] EXCLUDED_MODULES = ('horizon.test', 'openstack_dashboard.enabled', 'openstack_dashboard.test', 'openstack_dashboard.openstack.common', ) CURRENT_SOURCES = {} if not(os.path.exists(RSTDIR)): os.mkdir(RSTDIR) CURRENT_SOURCES[RSTDIR] = ['autoindex.rst'] INDEXOUT = open(os.path.join(RSTDIR, "autoindex.rst"), "w") INDEXOUT.write(""" ================= Source Code Index ================= .. contents:: :depth: 1 :local: """) for modulename, path in SRCS: sys.stdout.write("Generating source documentation for %s\n" % modulename) INDEXOUT.write("\n%s\n" % modulename.capitalize()) INDEXOUT.write("%s\n" % ("=" * len(modulename),)) INDEXOUT.write(".. toctree::\n") INDEXOUT.write(" :maxdepth: 1\n") INDEXOUT.write("\n") MOD_DIR = os.path.join(RSTDIR, modulename) CURRENT_SOURCES[MOD_DIR] = [] if not(os.path.exists(MOD_DIR)): os.mkdir(MOD_DIR) for module in find_autodoc_modules(modulename, path): if any([module.startswith(exclude) for exclude in EXCLUDED_MODULES]): print("Excluded module %s." % module) continue mod_path = os.path.join(path, *module.split(".")) generated_file = os.path.join(MOD_DIR, "%s.rst" % module) INDEXOUT.write(" %s/%s\n" % (modulename, module)) # Find the __init__.py module if this is a directory if os.path.isdir(mod_path): source_file = ".".join((os.path.join(mod_path, "__init__"), "py",)) else: source_file = ".".join((os.path.join(mod_path), "py")) CURRENT_SOURCES[MOD_DIR].append("%s.rst" % module) # Only generate a new file if the source has changed or we don't # have a doc file to begin with. if not os.access(generated_file, os.F_OK) or ( os.stat(generated_file).st_mtime < os.stat(source_file).st_mtime): print("Module %s updated, generating new documentation." % module) FILEOUT = open(generated_file, "w") header = "The :mod:`%s` Module" % module FILEOUT.write("%s\n" % ("=" * len(header),)) FILEOUT.write("%s\n" % header) FILEOUT.write("%s\n" % ("=" * len(header),)) FILEOUT.write(".. automodule:: %s\n" % module) FILEOUT.write(" :members:\n") FILEOUT.write(" :undoc-members:\n") FILEOUT.write(" :show-inheritance:\n") FILEOUT.write(" :noindex:\n") FILEOUT.close() INDEXOUT.close() # Delete auto-generated .rst files for sources which no longer exist for directory, subdirs, files in list(os.walk(RSTDIR)): for old_file in files: if old_file not in CURRENT_SOURCES.get(directory, []): print("Removing outdated file for %s" % old_file) os.remove(os.path.join(directory, old_file)) write_autodoc_index() # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ---------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. # They can be extensions coming with Sphinx (named 'sphinx.ext.*') # or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'oslosphinx', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Horizon' copyright = u'2012, OpenStack Foundation' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = horizon.version.version_info.version_string() # The full version, including alpha/beta/rc tags. release = horizon.version.version_info.release_string() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['**/#*', '**~', '**/#*#'] # The reST default role (used for this markup: `text`) # to use for all documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. modindex_common_prefix = ['horizon.', 'openstack_dashboard.'] primary_domain = 'py' nitpicky = False # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme_path = ['.'] # html_theme = '_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { "nosidebar": "false" } # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' git_cmd = "git log --pretty=format:'%ad, commit %h' --date=local -n1" html_last_updated_fmt = os.popen(git_cmd).read() # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Horizondoc' # -- Options for LaTeX output ------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', 'Horizon.tex', u'Horizon Documentation', u'OpenStack Foundation', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output ------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'horizon', u'Horizon Documentation', [u'OpenStack'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ----------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Horizon', u'Horizon Documentation', u'OpenStack', 'Horizon', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # -- Options for Epub output -------------------------------------------------- # Bibliographic Dublin Core info. epub_title = u'Horizon' epub_author = u'OpenStack' epub_publisher = u'OpenStack' epub_copyright = u'2012, OpenStack' # The language of the text. It defaults to the language option # or en if the language is not set. # epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. # epub_scheme = '' # The unique identifier of the text. This can be an ISBN number # or the project homepage. # epub_identifier = '' # A unique identification for the text. # epub_uid = '' # A tuple containing the cover image and cover page html template filenames. # epub_cover = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_post_files = [] # A list of files that should not be packed into the epub file. # epub_exclude_files = [] # The depth of the table of contents in toc.ncx. # epub_tocdepth = 3 # Allow duplicate toc entries. # epub_tocdup = True horizon-9.0.0/doc/source/quickstart.rst0000664000567000056710000002435112701407063021347 0ustar jenkinsjenkins00000000000000========== Quickstart ========== .. Note :: This section has been tested for Horizon on Ubuntu (12.04-64) and RPM-based (RHEL 7.x) distributions. Feel free to add notes and any changes according to your experiences or operating system. Linux Systems ============= Install the prerequisite packages. On Ubuntu:: > sudo apt-get install git python-dev python-virtualenv libssl-dev libffi-dev On RPM-based distributions (e.g., Fedora/RHEL/CentOS/Scientific Linux):: > sudo yum install gcc git-core python-devel python-virtualenv openssl-devel libffi-devel which Setup ===== To setup a Horizon development environment simply clone the Horizon git repository from http://github.com/openstack/horizon and execute the ``run_tests.sh`` script from the root folder (see :doc:`ref/run_tests`):: > git clone https://github.com/openstack/horizon.git > cd horizon > ./run_tests.sh .. note:: Running ``run_tests.sh`` will build a virtualenv, ``.venv``, where all the python dependencies for Horizon are installed and referenced. After the dependencies are installed, the unit test suites in the Horizon repo will be executed. There should be no errors from the tests. Next you will need to setup your Django application config by copying ``openstack_dashboard/local/local_settings.py.example`` to ``openstack_dashboard/local/local_settings.py``. To do this quickly you can use the following command:: > cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py .. note:: To add new settings or customize existing settings, modify the ``local_settings.py`` file. Horizon assumes a single end-point for OpenStack services which defaults to the local host (127.0.0.1), as is the default in DevStack. If this is not the case change the ``OPENSTACK_HOST`` setting in the ``openstack_dashboard/local/local_settings.py`` file, to the actual IP address of the OpenStack end-point Horizon should use. You can save changes you made to ``openstack_dashboard/local/local_settings.py`` with the following command:: > python manage.py migrate_settings --gendiff .. note:: This creates a ``local_settings.diff`` file which is a diff between ``local_settings.py`` and ``local_settings.py.example`` If you upgrade Horizon, you might need to update your ``openstack_dashboard/local/local_settings.py`` file with new parameters from ``openstack_dashboard/local/local_settings.py.example`` to do so, first update Horizon:: > git remote update && git pull --ff-only origin master Then update your ``openstack_dashboard/local/local_settings.py`` file:: > mv openstack_dashboard/local/local_settings.py openstack_dashboard/local/local_settings.py.old > python manage.py migrate_settings .. note:: This applies ``openstack_dashboard/local/local_settings.diff`` on ``openstack_dashboard/local/local_settings.py.example`` to regenerate an ``openstack_dashboard/local/local_settings.py`` file. The migration can sometimes have difficulties to migrate some settings, if this happens you will be warned with a conflict message pointing to an ``openstack_dashboard/local/local_settings.py_Some_DateTime.rej`` file. In this file, you will see the lines which could not be automatically changed and you will have to redo only these few changes manually instead of modifying the full ``openstack_dashboard/local/local_settings.py.example`` file. When all settings have been migrated, it is safe to regenerate a clean diff in order to prevent Conflicts for future migrations:: > mv openstack_dashboard/local/local_settings.diff openstack_dashboard/local/local_settings.diff.old > python manage.py migrate_settings --gendiff To start the Horizon development server use ``run_tests.sh``:: > ./run_tests.sh --runserver localhost:9000 .. note:: The default port for runserver is 8000 which is already consumed by heat-api-cfn in DevStack. If not running in DevStack `./run_tests.sh --runserver` will start the test server at `http://localhost:8000`. .. note:: The ``run_tests.sh`` script provides wrappers around ``manage.py``. For more information on manage.py which is a django, see `https://docs.djangoproject.com/en/dev/ref/django-admin/` Once the Horizon server is running, point a web browser to http://localhost:9000 or to the IP and port the server is listening for. .. note:: The ``DevStack`` project (http://devstack.org/) can be used to install an OpenStack development environment from scratch. For a local.conf that enables most services that Horizon supports managing see :doc:`local.conf ` .. note:: The minimum required set of OpenStack services running includes the following: * Nova (compute, api, scheduler, and network) * Glance * Keystone * Neutron (unless nova-network is used) Horizon provides optional support for other services. See :ref:`system-requirements-label` for the supported services. If Keystone endpoint for a service is configured, Horizon detects it and enables its support automatically. Editing Horizon's Source ======================== Although DevStack installs and configures an instance of Horizon when running stack.sh, the preferred development setup follows the instructions above on the server/VM running DevStack. There are several advantages to maintaining a separate copy of the Horizon repo, rather than editing the devstack installed copy. * Source code changes aren't as easily lost when running unstack.sh/stack.sh * The development server picks up source code changes (other than JavaScript and CSS due to compression and compilation) while still running. * Log messages and print statements go directly to the console. * Debugging with pdb becomes much simpler to interact with. .. Note:: JavaScript and CSS changes require a development server restart. Also, forcing a refresh of the page (e.g. using Shift-F5) in the browser is required to pull down non-cached versions of the CSS and JavaScript. The default setting in Horizon is to do compilation and compression of these files at server startup. If you have configured your local copy to do offline compression, more steps are required. Horizon's Structure =================== This project is a bit different from other OpenStack projects in that it has two very distinct components underneath it: ``horizon``, and ``openstack_dashboard``. The ``horizon`` directory holds the generic libraries and components that can be used in any Django project. The ``openstack_dashboard`` directory contains a reference Django project that uses ``horizon``. For development, both pieces share an environment which (by default) is built with the ``tools/install_venv.py`` script. That script creates a virtualenv and installs all the necessary packages. If dependencies are added to either ``horizon`` or ``openstack_dashboard``, they should be added to ``requirements.txt``. Project ======= Dashboard configuration ----------------------- To add a new dashboard to your project, you need to add a configuration file to ``openstack_dashboard/local/enabled`` directory. For more information on this, see :ref:`pluggable-settings-label`. There is also an alternative way to add a new dashboard, by adding it to Django's ``INSTALLED_APPS`` setting. For more information about this, see :ref:`dashboards`. However, please note that the recommended way is to take advantage of the pluggable settings feature. URLs ---- Then you add a single line to your project's ``urls.py``:: url(r'', include(horizon.urls)), Those urls are automatically constructed based on the registered Horizon apps. If a different URL structure is desired it can be constructed by hand. Templates --------- Pre-built template tags generate navigation. In your ``nav.html`` template you might have the following:: {% load horizon %} And in your ``sidebar.html`` you might have:: {% load horizon %} These template tags are aware of the current "active" dashboard and panel via template context variables and will render accordingly. Application =========== Structure --------- An application would have the following structure (we'll use project as an example):: project/ |---__init__.py |---dashboard.py <-----Registers the app with Horizon and sets dashboard properties |---overview/ |---images/ |-- images |-- __init__.py |---panel.py <-----Registers the panel in the app and defines panel properties |-- snapshots/ |-- templates/ |-- tests.py |-- urls.py |-- views.py ... ... Dashboard Classes ----------------- Inside of ``dashboard.py`` you would have a class definition and the registration process:: import horizon .... # ObjectStorePanels is an example for a PanelGroup # for panel classes in general, see below class ObjectStorePanels(horizon.PanelGroup): slug = "object_store" name = _("Object Store") panels = ('containers',) class Project(horizon.Dashboard): name = _("Project") # Appears in navigation slug = "project" # Appears in URL # panels may be strings or refer to classes, such as # ObjectStorePanels panels = (BasePanels, NetworkPanels, ObjectStorePanels) default_panel = 'overview' ... horizon.register(Project) Panel Classes ------------- To connect a :class:`~horizon.Panel` with a :class:`~horizon.Dashboard` class you register it in a ``panel.py`` file like so:: import horizon from openstack_dashboard.dashboards.project import dashboard class Images(horizon.Panel): name = "Images" slug = 'images' permissions = ('openstack.roles.admin', 'my.other.permission',) # You could also register your panel with another application's dashboard dashboard.Project.register(Images) By default a :class:`~horizon.Panel` class looks for a ``urls.py`` file in the same directory as ``panel.py`` to include in the rollup of url patterns from panels to dashboards to Horizon, resulting in a wholly extensible, configurable URL structure. horizon-9.0.0/doc/source/contributing.rst0000664000567000056710000005444012701407063021666 0ustar jenkinsjenkins00000000000000================== Contributing Guide ================== First and foremost, thank you for wanting to contribute! It's the only way open source works! Before you dive into writing patches, here are some of the basics: * Project page: http://launchpad.net/horizon * Bug tracker: https://bugs.launchpad.net/horizon * Source code: https://github.com/openstack/horizon * Code review: https://review.openstack.org/#q,status:open+project:openstack/horizon,n,z * Continuous integration: * Jenkins: https://jenkins.openstack.org * Zuul: http://status.openstack.org/zuul * IRC Channel: #openstack-horizon on Freenode. Making Contributions ==================== Getting Started --------------- We'll start by assuming you've got a working checkout of the repository (if not then please see the :doc:`quickstart`). Second, you'll need to take care of a couple administrative tasks: #. Create an account on Launchpad. #. Sign the `OpenStack Contributor License Agreement`_ and follow the associated instructions to verify your signature. #. Join the `Horizon Developers`_ team on Launchpad. #. Follow the `instructions for setting up git-review`_ in your development environment. Whew! Got all that? Okay! You're good to go. .. _`OpenStack Contributor License Agreement`: http://wiki.openstack.org/CLA .. _`Horizon Developers`: https://launchpad.net/~horizon .. _`instructions for setting up git-review`: http://docs.openstack.org/infra/manual/developers.html#development-workflow Ways To Contribute ------------------ The easiest way to get started with Horizon's code is to pick a bug on Launchpad that interests you, and start working on that. Bugs tagged as ``low-hanging-fruit`` are a good place to start. Alternatively, if there's an OpenStack API feature you would like to see implemented in Horizon feel free to try building it. If those are too big, there are lots of great ways to get involved without plunging in head-first: * Report bugs, triage new tickets, and review old tickets on the `bug tracker`_. * Propose ideas for improvements via `Launchpad Blueprints`_, via the mailing list on the project page, or on IRC. * Write documentation! * Write unit tests for untested code! * Help improve the `User Experience Design`_ or contribute to the `Persona Working Group`_. .. _`bug tracker`: https://bugs.launchpad.net/horizon .. _`Launchpad Blueprints`: https://blueprints.launchpad.net/horizon .. _`User Experience Design`: https://wiki.openstack.org/wiki/UX#Getting_Started .. _`Persona Working Group`: https://wiki.openstack.org/wiki/Personas Choosing Issues To Work On -------------------------- In general, if you want to write code, there are three cases for issues you might want to work on: #. Confirmed bugs #. Approved blueprints (features) #. New bugs you've discovered If you have an idea for a new feature that isn't in a blueprint yet, it's a good idea to write the blueprint first, so you don't end up writing a bunch of code that may not go in the direction the community wants. For bugs, open the bug first, but if you can reproduce the bug reliably and identify its cause then it's usually safe to start working on it. However, getting independent confirmation (and verifying that it's not a duplicate) is always a good idea if you can be patient. After You Write Your Patch -------------------------- Once you've made your changes, there are a few things to do: * Make sure the unit tests pass: ``./run_tests.sh`` for Python, and ``npm run test`` for JS. * Make sure the linting tasks pass: ``./run_tests.sh --pep8`` for Python, and ``npm run lint`` for JS. * Make sure your code is ready for translation: ``./run_tests.sh --pseudo de`` See the Translatability section below for details. * Make sure your code is up-to-date with the latest master: ``git pull --rebase`` * Finally, run ``git review`` to upload your changes to Gerrit for review. The Horizon core developers will be notified of the new review and will examine it in a timely fashion, either offering feedback or approving it to be merged. If the review is approved, it is sent to Jenkins to verify the unit tests pass and it can be merged cleanly. Once Jenkins approves it, the change will be merged to the master repository and it's time to celebrate! Etiquette ========= The community's guidelines for etiquette are fairly simple: * Treat everyone respectfully and professionally. * If a bug is "in progress" in the bug tracker, don't start working on it without contacting the author. Try on IRC, or via the launchpad email contact link. If you don't get a response after a reasonable time, then go ahead. Checking first avoids duplicate work and makes sure nobody's toes get stepped on. * If a blueprint is assigned, even if it hasn't been started, be sure you contact the assignee before taking it on. These larger issues often have a history of discussion or specific implementation details that the assignee may be aware of that you are not. * Please don't re-open tickets closed by a core developer. If you disagree with the decision on the ticket, the appropriate solution is to take it up on IRC or the mailing list. * Give credit where credit is due; if someone helps you substantially with a piece of code, it's polite (though not required) to thank them in your commit message. .. _translatability: Translatability =============== Horizon gets translated into multiple languages. The pseudo translation tool can be used to verify that code is ready to be translated. The pseudo tool replaces a language's translation with a complete, fake translation. Then you can verify that your code properly displays fake translations to validate that your code is ready for translation. Running the pseudo translation tool ----------------------------------- #. Make sure your English po file is up to date: ``./run_tests.sh --makemessages`` #. Run the pseudo tool to create pseudo translations. For example, to replace the German translation with a pseudo translation: ``./run_tests.sh --pseudo de`` #. Compile the catalog: ``./run_tests.sh --compilemessages`` #. Run your development server. #. Log in and change to the language you pseudo translated. It should look weird. More specifically, the translatable segments are going to start and end with a bracket and they are going to have some added characters. For example, "Log In" will become "[~Log In~您好Ñшçã‚]" This is useful because you can inspect for the following, and consider if your code is working like it should: * If you see a string in English it's not translatable. Should it be? * If you see brackets next to each other that might be concatenation. Concatenation can make quality translations difficult or impossible. See https://wiki.openstack.org/wiki/I18n/TranslatableStrings#Use_string_formating_variables.2C_never_perform_string_concatenation for additional information. * If there is unexpected wrapping/truncation there might not be enough space for translations. * If you see a string in the proper translated language, it comes from an external source. (That's not bad, just sometimes useful to know) * If you get new crashes, there is probably a bug. Don't forget to cleanup any pseudo translated po files. Those don't get merged! Code Style ========== As a project, Horizon adheres to code quality standards. Python ------ We follow PEP8_ for all our Python code, and use ``pep8.py`` (available via the shortcut ``./run_tests.sh --pep8``) to validate that our code meets proper Python style guidelines. .. _PEP8: http://www.python.org/dev/peps/pep-0008/ Django ------ Additionally, we follow `Django's style guide`_ for templates, views, and other miscellany. .. _Django's style guide: https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/ JavaScript ---------- The following standards are divided into required and recommended sections. Our main goal in establishing these best practices is to have code that is reliable, readable, and maintainable. Required ~~~~~~~~ **Reliable** * The code has to work on the stable and latest versions of Firefox, Chrome, Safari, and Opera web browsers, and on Microsoft Internet Explorer 11 and later. * If you turned compression off during development via ``COMPRESS_ENABLED = False`` in local_settings.py, re-enable compression and test your code before submitting. * Use ``===`` as opposed to ``==`` for equality checks. The ``==`` will do a type cast before comparing, which can lead to unwanted results. .. Note :: If typecasting is desired, explicit casting is preferred to keep the meaning of your code clear. * Keep document reflows to a minimum. DOM manipulation is expensive, and can become a performance issue. If you are accessing the DOM, make sure that you are doing it in the most optimized way. One example is to build up a document fragment and then append the fragment to the DOM in one pass instead of doing multiple smaller DOM updates. * Use “strictâ€, enclosing each JavaScript file inside a self-executing function. The self-executing function keeps the strict scoped to the file, so its variables and methods are not exposed to other JavaScript files in the product. .. Note :: Using strict will throw exceptions for common coding errors, like accessing global vars, that normally are not flagged. Example: :: (function(){ 'use strict'; // code... })(); * Use ``forEach`` | ``each`` when looping whenever possible. AngularJS and jQuery both provide for each loops that provide both iteration and scope. AngularJS: :: angular.forEach(objectToIterateOver, function(value, key) { // loop logic }); jQuery: :: $.each(objectToIterateOver, function(key, value) { // loop logic }); * Do not put variables or functions in the global namespace. There are several reasons why globals are bad, one being that all JavaScript included in an application runs in the same scope. The issue with that is if another script has the same method or variable names they overwrite each other. * Always put ``var`` in front of your variables. Not putting ``var`` in front of a variable puts that variable into the global space, see above. * Do not use ``eval( )``. The eval (expression) evaluates the expression passed to it. This can open up your code to security vulnerabilities and other issues. * Do not use '``with`` object {code}'. The ``with`` statement is used to access properties of an object. The issue with ``with`` is that its execution is not consistent, so by reading the statement in the code it is not always clear how it is being used. **Readable & Maintainable** * Give meaningful names to methods and variables. * Avoid excessive nesting. * Avoid HTML and CSS in JS code. HTML and CSS belong in templates and stylesheets respectively. For example: * In our HTML files, we should focus on layout. 1. Reduce the small/random `` {% for file in HORIZON_CONFIG.js_files %} {% endfor %} {% block source %} {% endblock %} {% block spec %} {% for file in HORIZON_CONFIG.js_spec_files %} {% endfor %} {% endblock %} horizon-9.0.0/horizon/templates/horizon/jasmine/jasmine_legacy.html0000664000567000056710000002453112701407063027022 0ustar jenkinsjenkins00000000000000{% extends "horizon/jasmine/jasmine.html" %} {% block source %} {% include "horizon/_script_i18n.html" %} {% include "horizon/client_side/templates.html" %} {% endblock %} {% block spec %} {% endblock %} {% block content %}
cat1
dog1
cat2
dog2
Displaying 4 items
cat1
Displaying 4 items

Flavors

Flavor Name VCPU RAM (MB) Root Disk (GB) Ephemeral Disk (GB) Swap Disk (MB) Max. VMs Delete
-
-
-
Displaying 3 items
{% endblock %} horizon-9.0.0/horizon/templates/horizon/jasmine/index.html0000664000567000056710000000053712701407063025157 0ustar jenkinsjenkins00000000000000 Jasmine Spec Runner Index

Available tests

horizon-9.0.0/horizon/templates/horizon/common/0000775000567000056710000000000012701407231023014 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/templates/horizon/common/_workflow_step.html0000664000567000056710000000033012701407063026745 0ustar jenkinsjenkins00000000000000
{% include "horizon/common/_form_fields.html" %}
{{ step.get_help_text }}
horizon-9.0.0/horizon/templates/horizon/common/_form_field.html0000664000567000056710000000466312701407063026163 0ustar jenkinsjenkins00000000000000{% load form_helpers %}
{% if field|is_checkbox %}
{% include 'horizon/common/fields/_themable_checkbox.html' %}
{% elif field|is_radio %} {% if field.auto_id %} {% if field.field.required %}{% include "horizon/common/_form_field_required.html" %}{% endif %} {% if field.help_text %} {% endif %} {% endif %}
{% for choice in field %}
{% endfor %} {% for error in field.errors %} {{ error }} {% endfor %}
{% else %} {% if field.auto_id %} {% if field.field.required %}{% include "horizon/common/_form_field_required.html" %}{% endif %} {% if field.help_text %} {% endif %} {% endif %}
{% with add_item_link=field|add_item_url %} {% if add_item_link %}
{{ field|add_bootstrap_class }}
{% else %} {{ field|add_bootstrap_class }} {% endif %} {% endwith %} {% for error in field.errors %} {{ error }} {% endfor %}
{% endif %}
horizon-9.0.0/horizon/templates/horizon/common/_data_table_action.html0000664000567000056710000000263512701407063027467 0ustar jenkinsjenkins00000000000000{% load horizon %} {% minifyspace %} {% if action.method != "GET" %} {% else %} {% if action.icon != None %} {% endif %} {% else %} href="{{ action.bound_url }}"> {% endif %} {{ action.verbose_name }} {% endif %} {% endminifyspace %} horizon-9.0.0/horizon/templates/horizon/common/_horizontal_fields.html0000664000567000056710000000027512701407063027567 0ustar jenkinsjenkins00000000000000{% include 'horizon/common/_form_errors.html' with form=form %} {% for field in form.visible_fields %} {% include 'horizon/common/_horizontal_field.html' with field=field %} {% endfor %} horizon-9.0.0/horizon/templates/horizon/common/_modal_form_add_members.html0000664000567000056710000000164512701407063030513 0ustar jenkinsjenkins00000000000000{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body %} {% endblock %} {% block modal-js %} {% endblock %} horizon-9.0.0/horizon/templates/horizon/common/_breadcrumb_nav.html0000664000567000056710000000123112701407063027013 0ustar jenkinsjenkins00000000000000{% load truncate_filter %} horizon-9.0.0/horizon/templates/horizon/common/_breadcrumb.html0000664000567000056710000000074112701407063026154 0ustar jenkinsjenkins00000000000000{% load i18n %} {% with subfolders=breadcrumb.get_subfolders %} {% endwith %} horizon-9.0.0/horizon/templates/horizon/common/_region_selector.html0000664000567000056710000000115512701407063027231 0ustar jenkinsjenkins00000000000000{% if regions.support %} {% endif %} horizon-9.0.0/horizon/templates/horizon/common/_formset_table.html0000664000567000056710000000257712701407063026705 0ustar jenkinsjenkins00000000000000{% extends 'horizon/common/_data_table.html' %} {% load i18n %} {% block table_columns %} {% if not table.is_browser_table %} {% for column in columns %} {{ column }} {% endfor %} {% endif %} {% endblock table_columns %} {% block table %} {% with table.get_formset as formset %} {{ formset.management_form }} {% if formset.non_field_errors %}
{{ formset.non_field_errors }}
{% endif %} {% endwith %} {{ block.super }} {% endblock table %} horizon-9.0.0/horizon/templates/horizon/common/_formset_table_row.html0000664000567000056710000000144612701407063027566 0ustar jenkinsjenkins00000000000000{% load i18n %} {% for cell in row %} {% if cell.field %} {{ cell.field }} {% else %} {% if cell.wrap_list %}
    {% endif %}{{ cell.value }}{% if cell.wrap_list %}
{% endif %} {% endif %} {% if forloop.first %} {% for field in row.form.hidden_fields %} {{ field }} {% for error in field.errors %} {% blocktrans with name=field.name %}{{ name }}: {{ error }}{% endblocktrans %} {% endfor %} {% endfor %} {% if row.form.non_field_errors %}
{{ row.form.non_field_errors }}
{% endif %} {% endif %} {% endfor %} horizon-9.0.0/horizon/templates/horizon/common/_data_table.html0000664000567000056710000000604212701407063026126 0ustar jenkinsjenkins00000000000000{% load i18n %} {% with table.needs_form_wrapper as needs_form_wrapper %}
{% if needs_form_wrapper %}
{% csrf_token %}{% endif %} {% with columns=table.get_columns rows=table.get_rows %} {% block table %} {% block table_caption %} {% endblock table_caption %} {% block table_breadcrumb %} {% if table.breadcrumb %} {% endif %} {% endblock table_breadcrumb %} {% block table_columns %} {% if not table.is_browser_table %} {% for column in columns %} {% endfor %} {% endif %} {% endblock table_columns %} {% block table_body %} {% for row in rows %} {{ row.render }} {% empty %} {% endfor %} {% endblock table_body %} {% block table_footer %} {% if table.footer and rows %} {% if table.needs_summary_row %} {% for column in columns %} {% if forloop.first %} {% else %} {% endif %} {% endfor %} {% endif %} {% endif %} {% endblock table_footer %}
{% if not hidden_title %} {{ table }} {% endif %} {{ table.render_table_actions }}
{{ column }} {% if column.help_text %} {% endif %}
{{ table.get_empty_message }}
{% trans "Summary" %}{{ column.get_summation|default_if_none:"–"}}
{% blocktrans count counter=rows|length %}Displaying {{ counter }} item{% plural %}Displaying {{ counter }} items{% endblocktrans %} {% if table.has_prev_data or table.has_more_data %} | {% endif %} {% if table.has_prev_data %} {% trans "« Prev" %} {% endif %} {% if table.has_more_data %} {% trans "Next »" %} {% endif %}
{% endblock table %} {% endwith %} {% if needs_form_wrapper %}
{% endif %}
{% endwith %} horizon-9.0.0/horizon/templates/horizon/common/_data_table_row_actions_dropdown.html0000664000567000056710000000176212701407063032455 0ustar jenkinsjenkins00000000000000{% load horizon i18n %} {% spaceless %} {# This makes sure whitespace doesn't affect positioning for dropdown. #} {% if row_actions|length == 1 %} {% include "horizon/common/_data_table_action.html" with action=row_actions.0 is_single=1 %} {% elif row_actions|length > 1 %}
{% for action in row_actions %} {% if forloop.first %} {% include "horizon/common/_data_table_action.html" with is_small=1 is_single=1 %} {% endif %} {% endfor %}
{% endif %} {% endspaceless %} horizon-9.0.0/horizon/templates/horizon/common/_workflow_base.html0000664000567000056710000000046412701407063026714 0ustar jenkinsjenkins00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans workflow.name %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=workflow.name %} {% endblock page_header %} {% block main %} {% include 'horizon/common/_workflow.html' %} {% endblock %} horizon-9.0.0/horizon/templates/horizon/common/_data_table_row_action_row.html0000664000567000056710000000104412701407063031236 0ustar jenkinsjenkins00000000000000{% if action.method != "GET" %} {% else %} {% if action.icon != None %} {% endif %} {{ action.verbose_name }} {% endif %} horizon-9.0.0/horizon/templates/horizon/common/_sidebar_module.html0000664000567000056710000000043612701407063027025 0ustar jenkinsjenkins00000000000000{% for module in modules %}

{{ module.title }}

{% endfor %} horizon-9.0.0/horizon/templates/horizon/common/_page_header.html0000664000567000056710000000044712701407063026275 0ustar jenkinsjenkins00000000000000{% load i18n %} {% block page_header %} {% endblock %} horizon-9.0.0/horizon/templates/horizon/common/_resource_browser.html0000664000567000056710000000126712701407063027444 0ustar jenkinsjenkins00000000000000{% load i18n %}
{{ browser.content_table.render }}
{% blocktrans count nav_items=browser.navigation_table.data|length %}Displaying {{ nav_items }} item{% plural %}Displaying {{ nav_items }} items{% endblocktrans %} {% blocktrans count content_items=browser.content_table.data|length %}Displaying {{ content_items }} item{% plural %}Displaying {{ content_items }} items{% endblocktrans %}
horizon-9.0.0/horizon/templates/horizon/common/_data_table_row_actions_row.html0000664000567000056710000000035012701407063031420 0ustar jenkinsjenkins00000000000000{% load i18n %}
{% block table_actions %} {% for action in row_actions %} {% include "horizon/common/_data_table_row_action_row.html" %} {% endfor %} {% endblock table_actions %}
horizon-9.0.0/horizon/templates/horizon/common/_workflow.html0000664000567000056710000000627712701407063025732 0ustar jenkinsjenkins00000000000000{% load i18n %} {% with workflow.get_entry_point as entry_point %}
{% csrf_token %} {% if REDIRECT_URL %}{% endif %}
{% endwith %} {% block modal-js %} {% if workflow.wizard %} {% endif %} {% endblock %} horizon-9.0.0/horizon/templates/horizon/common/_sidebar.html0000664000567000056710000000012012701407063025446 0ustar jenkinsjenkins00000000000000{% load branding horizon i18n %} horizon-9.0.0/horizon/templates/horizon/common/_modal_form.html0000664000567000056710000000372212701407063026167 0ustar jenkinsjenkins00000000000000{% extends "horizon/common/_modal.html" %} {% block content %} {% if table %} {% endif %}
{% csrf_token %}
{% endblock %} horizon-9.0.0/horizon/templates/horizon/common/_form_field_required.html0000664000567000056710000000006612701407063030054 0ustar jenkinsjenkins00000000000000 horizon-9.0.0/horizon/templates/horizon/common/_modal.html0000664000567000056710000000165012701407063025142 0ustar jenkinsjenkins00000000000000 {% block modal-js %} {% endblock %} horizon-9.0.0/horizon/templates/horizon/common/_usage_summary.html0000664000567000056710000000261012701407063026724 0ustar jenkinsjenkins00000000000000{% load i18n sizeformat %}

{% trans "Usage Summary" %}

{% trans "Select a period of time to query its usage:" %}

{% blocktrans with start=form.start %} {{ start }}{% endblocktrans %}
{% blocktrans with end=form.end %} {{ end }}{% endblocktrans %}
{% trans "The date should be in YYYY-mm-dd format." %}

{% trans "Active Instances:" %} {{ usage.summary.instances|default:'0' }} {% trans "Active RAM:" %} {{ usage.summary.memory_mb|mb_float_format|default:'0' }} {% trans "This Period's VCPU-Hours:" %} {{ usage.summary.vcpu_hours|floatformat:2|default:'0' }} {% trans "This Period's GB-Hours:" %} {{ usage.summary.disk_gb_hours|floatformat:2|default:'0' }} {% trans "This Period's RAM-Hours:" %} {{ usage.summary.memory_mb_hours|floatformat:2|default:'0' }}

horizon-9.0.0/horizon/templates/horizon/common/fields/0000775000567000056710000000000012701407231024262 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/templates/horizon/common/fields/_themable_checkbox.html0000664000567000056710000000147612701407063030751 0ustar jenkinsjenkins00000000000000
{% if field.auto_id %} {{ field }} {% endif %} {% for error in field.errors %} {{ error }} {% endfor %}
horizon-9.0.0/horizon/templates/horizon/common/_data_table_cell.html0000664000567000056710000000347112701407063027130 0ustar jenkinsjenkins00000000000000{% if cell.inline_edit_mod and cell.update_allowed %}
{{ cell.value }} {% if cell.column.form_field.label %} {% endif %}
{% else %} {% if cell.inline_edit_available and cell.update_allowed %}
{% if cell.wrap_list %}
    {% endif %}{{ cell.value }}{% if cell.wrap_list %}
{% endif %}
{% else %} {% if cell.wrap_list %}
    {% endif %}{{ cell.value }}{% if cell.wrap_list %}
{% endif %} {% endif %} {% endif %} horizon-9.0.0/horizon/templates/horizon/common/_horizontal_field.html0000664000567000056710000000222412701407063027400 0ustar jenkinsjenkins00000000000000{% load form_helpers %}
{% if field|is_checkbox %} {% with is_vertical=1 %} {% include 'horizon/common/fields/_themable_checkbox.html' %} {% endwith %} {% else %} {{ field|add_bootstrap_class }} {% endif %} {% for error in field.errors %} {{ error }} {% empty %} {% comment %} Escape help_text a second time here, to avoid an XSS issue in bootstrap.js. This can most likely be removed once we upgrade bootstrap.js past 2.0.2. Note: the spaces are necessary here. {% endcomment %} {% if field.help_text %} {% filter force_escape %} {{ field.help_text }} {% endfilter %} {% endif %} {% endfor %}
horizon-9.0.0/horizon/templates/horizon/common/_domain_page_header.html0000664000567000056710000000047412701407063027624 0ustar jenkinsjenkins00000000000000{% load i18n %} {% block page_header %} {% endblock %} horizon-9.0.0/horizon/templates/horizon/common/_workflow_step_update_members.html0000664000567000056710000000475412701407063032037 0ustar jenkinsjenkins00000000000000{% load i18n %}
{% include "horizon/common/_form_fields.html" %}
horizon-9.0.0/horizon/templates/horizon/common/_detail_table.html0000664000567000056710000000002312701407063026450 0ustar jenkinsjenkins00000000000000{{ table.render }} horizon-9.0.0/horizon/templates/horizon/common/_limit_summary.html0000664000567000056710000000234612701407063026744 0ustar jenkinsjenkins00000000000000{% load i18n horizon humanize sizeformat %} {% spaceless %}

{% trans "Limit Summary" %}

{% for quota in charts %} {% if forloop.last or forloop.counter0|divisibleby:6 %}
{% endif %} {% if forloop.first or forloop.counter0|divisibleby:6 %}
{% endif %}
{{ quota.name }}
{% if quota.max|quotainf != '-1' %} {% blocktrans with used=quota.used|intcomma available=quota.max|quotainf|intcomma %} Used {{ used }} of {{ available }} {% endblocktrans %} {% else %} {% blocktrans with used=quota.used|intcomma %} Used {{ used }} (No Limit) {% endblocktrans %} {% endif %}
{% endfor %}
{% endspaceless %} horizon-9.0.0/horizon/templates/horizon/common/_data_table_table_actions.html0000664000567000056710000000545712701407063031026 0ustar jenkinsjenkins00000000000000{% load i18n %}
{% block table_filter %} {% if filter.filter_type == 'fixed' %}
{% for button in filter.fixed_buttons %} {% endfor %}
{% elif filter.filter_type == 'query' %} {% elif filter.filter_type == 'server' %} {% endif %} {% endblock table_filter %} {% block table_actions %} {% comment %} For each single action in the Table Actions area {% endcomment %} {% for action in table_actions_buttons %} {% include "horizon/common/_data_table_action.html" with is_table_action=1 is_single=1 %} {% endfor %} {% comment %} If additional actions are defined, scoop them into the actions dropdown menu {% endcomment %} {% if table_actions_menu|length > 0 %}
{% if table_actions_buttons|length > 0 %} {% trans "More Actions" %} {% else %} {% trans "Actions" %} {% endif %}
{% endif %} {% endblock table_actions %}
horizon-9.0.0/horizon/templates/horizon/common/_tab_group.html0000664000567000056710000000134412701407063026030 0ustar jenkinsjenkins00000000000000{% with tab_group.get_tabs as tabs %} {% if tabs %} {# Tab Navigation #} {% if tab_group.show_single_tab or tabs|length > 1 %} {% endif %} {# Tab Content #}
{% for tab in tabs %}
{{ tab.render }}
{% endfor %}
{% endif %} {% endwith %} horizon-9.0.0/horizon/templates/horizon/common/_form_errors.html0000664000567000056710000000047612701407063026412 0ustar jenkinsjenkins00000000000000{% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %} {% if form.warnings %}
{{ form.warnings }}
{% endif %} {% if form.non_field_errors %}
{{ form.non_field_errors }}
{% endif %} horizon-9.0.0/horizon/templates/horizon/common/_data_table_row.html0000664000567000056710000000030412701407063027010 0ustar jenkinsjenkins00000000000000 {% spaceless %} {% for cell in row %} {% include "horizon/common/_data_table_cell.html" %} {% endfor %} {% endspaceless %} horizon-9.0.0/horizon/templates/horizon/common/_form_fields.html0000664000567000056710000000026712701407063026342 0ustar jenkinsjenkins00000000000000{% include 'horizon/common/_form_errors.html' with form=form %} {% for field in form.visible_fields %} {% include 'horizon/common/_form_field.html' with field=field %} {% endfor %} horizon-9.0.0/horizon/templates/horizon/common/_detail.html0000664000567000056710000000054112701407063025306 0ustar jenkinsjenkins00000000000000{% extends 'base.html' %} {% load i18n %} {% load breadcrumb_nav %} {% block title %} {{ page_title }} {% endblock %} {% block page_header %} {% endblock %} {% block main %}
{{ tab_group.render }}
{% endblock %} horizon-9.0.0/horizon/templates/horizon/client_side/0000775000567000056710000000000012701407231024006 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/templates/horizon/client_side/template.html0000664000567000056710000000014712701407063026514 0ustar jenkinsjenkins00000000000000 horizon-9.0.0/horizon/templates/horizon/client_side/_script_loader.html0000664000567000056710000000104412701407063027667 0ustar jenkinsjenkins00000000000000 horizon-9.0.0/horizon/templates/horizon/client_side/_modal.html0000664000567000056710000000146512701407063026140 0ustar jenkinsjenkins00000000000000{% extends "horizon/client_side/template.html" %} {% load horizon %} {% block id %}modal_template{% endblock %} {% block template %}{% spaceless %}{% jstemplate %} {% endjstemplate %}{% endspaceless %}{% endblock %} horizon-9.0.0/horizon/templates/horizon/client_side/_table_row.html0000664000567000056710000000045312701407063027016 0ustar jenkinsjenkins00000000000000{% extends "horizon/client_side/template.html" %} {% load horizon %} {% block id %}empty_row_template{% endblock %} {% block template %}{% spaceless %}{% jstemplate %} [[no_items_label]] {% endjstemplate %}{% endspaceless %}{% endblock %} horizon-9.0.0/horizon/templates/horizon/client_side/_loading.html0000664000567000056710000000063612701407063026460 0ustar jenkinsjenkins00000000000000{% extends "horizon/client_side/template.html" %} {% load i18n horizon %} {% block id %}spinner-modal{% endblock %} {% block template %}{% spaceless %}{% jstemplate %} {% endjstemplate %}{% endspaceless %}{% endblock %} horizon-9.0.0/horizon/templates/horizon/client_side/_membership.html0000664000567000056710000000173512701407063027177 0ustar jenkinsjenkins00000000000000{% extends "horizon/client_side/template.html" %} {% load horizon %} {% block id %}membership_template{% endblock %} {% block template %}{% spaceless %}{% jstemplate %} {% endjstemplate %}{% endspaceless %}{% endblock %} horizon-9.0.0/horizon/templates/horizon/client_side/templates.html0000664000567000056710000000040212701407063026671 0ustar jenkinsjenkins00000000000000{% include "horizon/client_side/_modal.html" %} {% include "horizon/client_side/_table_row.html" %} {% include "horizon/client_side/_alert_message.html" %} {% include "horizon/client_side/_loading.html" %} {% include "horizon/client_side/_membership.html" %}horizon-9.0.0/horizon/templates/horizon/client_side/_alert_message.html0000664000567000056710000000102612701407063027650 0ustar jenkinsjenkins00000000000000{% extends "horizon/client_side/template.html" %} {% load horizon %} {% block id %}alert_message_template{% endblock %} {% block template %}{% spaceless %}{% jstemplate %}

[[type_display]] [[#safe]] [[[message]]] [[/safe]] [[^safe]] [[message]] [[/safe]]

{% endjstemplate %}{% endspaceless %}{% endblock %} horizon-9.0.0/horizon/templates/horizon/_script_i18n.html0000664000567000056710000000023312701407063024715 0ustar jenkinsjenkins00000000000000{% comment %} Django's JavaScript i18n Implementation {% endcomment %} horizon-9.0.0/horizon/templates/bootstrap/0000775000567000056710000000000012701407231022051 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/templates/bootstrap/progress_bar.html0000664000567000056710000000136312701407063025435 0ustar jenkinsjenkins00000000000000{% load horizon %} {% minifyspace %}
{% for this_bar in bars %}
{% if not text %} {{ this_bar.percent }}% {% endif %}
{% endfor %}
{% endminifyspace %} horizon-9.0.0/horizon/templates/auth/0000775000567000056710000000000012701407231020775 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/templates/auth/_login_page.html0000664000567000056710000000066712701407063024142 0ustar jenkinsjenkins00000000000000{% extends 'auth/_login_form.html' %} {% load i18n %} {% block pre_login %} {% endblock %}horizon-9.0.0/horizon/templates/auth/_login_form.html0000664000567000056710000000502312701407063024160 0ustar jenkinsjenkins00000000000000{% load i18n %} {% block pre_login %}
{% csrf_token %} {% endblock %}
{% block login_header %} {% endblock %}
{% block login_body %} {% comment %} These fake fields are required to prevent Chrome v34+ from autofilling form. {% endcomment %} {% if HORIZON_CONFIG.password_autocomplete != "on" %} {%endif%} {% include "auth/_description.html" %}
{% if request.user.is_authenticated and 'next' in request.GET %}

{% trans "You do not have permission to access the resource:" %}

{{ request.GET.next }}

{% url 'horizon:user_home' as home_url %} {% blocktrans %} Login as different user or go back to home page {% endblocktrans %}

{% endif %} {% if request.COOKIES.logout_reason %}

{{ request.COOKIES.logout_reason }}

{% endif %} {% if next %} {% endif %} {% include "horizon/common/_form_fields.html" %}
{% endblock %}
{% block post_login%}
{% endblock %} horizon-9.0.0/horizon/templates/auth/_login_modal.html0000664000567000056710000000113612701407063024312 0ustar jenkinsjenkins00000000000000{% extends 'auth/_login_form.html' %} {% load i18n %} {% block pre_login %} {% endblock %}horizon-9.0.0/horizon/templates/auth/login.html0000664000567000056710000000042612701407063023000 0ustar jenkinsjenkins00000000000000{% extends "base.html" %} {% load i18n %} {% block title %}{% trans "Login" %}{% endblock %} {% block body_id %}splash{% endblock %} {% block content %} {% include 'auth/_login.html' %} {% endblock %} {% block footer %} {% include '_login_footer.html' %} {% endblock %} horizon-9.0.0/horizon/templates/auth/_splash.html0000664000567000056710000000017712701407063023324 0ustar jenkinsjenkins00000000000000{% load themes %}
horizon-9.0.0/horizon/templates/auth/_description.html0000664000567000056710000000061212701407063024347 0ustar jenkinsjenkins00000000000000{% load i18n %} {% comment %} This help text will only show up if websso is enabled because websso introduces new authentication mechanisms. {% endcomment %}
{% block websso-help-text %} {% blocktrans %} If you are not sure which authentication method to use, contact your administrator. {% endblocktrans %} {% endblock %}
horizon-9.0.0/horizon/templates/auth/_login.html0000664000567000056710000000027212701407063023136 0ustar jenkinsjenkins00000000000000{% load i18n %} {% if 'is_modal' in request.GET or 'is_modal' in request.POST %} {% include 'auth/_login_modal.html' %} {% else %} {% include 'auth/_login_page.html' %} {% endif %} horizon-9.0.0/horizon/contrib/0000775000567000056710000000000012701407231017476 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/contrib/bootstrap_datepicker.py0000664000567000056710000000306012701407063024262 0ustar jenkinsjenkins00000000000000# Copyright 2014 IBM Corp. # # 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. # Map Horizon languages to datepicker locales LOCALE_MAPPING = { 'ar': 'ar', 'az': 'az', 'bg': 'bg', 'ca': 'ca', 'cs': 'cs', 'cy': 'cy', 'da': 'da', 'de': 'de', 'el': 'el', 'es': 'es', 'et': 'et', 'fa': 'fa', 'fi': 'fi', 'fr': 'fr', 'gl': 'gl', 'he': 'he', 'hr': 'hr', 'hu': 'hu', 'id': 'id', 'is': 'is', 'it': 'it', 'ja': 'ja', 'ka': 'ka', 'kk': 'kk', 'ko': 'kr', # difference between horizon and datepicker 'lt': 'lt', 'lv': 'lv', 'mk': 'mk', 'ms': 'ms', 'nb': 'nb', 'nl-be': 'nl-BE', 'nl': 'nl', 'no': 'no', 'pl': 'pl', 'pt-br': 'pt-BR', 'pt': 'pt', 'ro': 'ro', 'rs-latin': 'rs-latin', 'sr': 'rs', # difference between horizon and datepicker 'ru': 'ru', 'sk': 'sk', 'sl': 'sl', 'sq': 'sq', 'sv': 'sv', 'sw': 'sw', 'th': 'th', 'tr': 'tr', 'ua': 'ua', 'vi': 'vi', 'zh-cn': 'zh-CN', 'zh-tw': 'zh-TW', } horizon-9.0.0/horizon/contrib/__init__.py0000664000567000056710000000000012701407063021600 0ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/templatetags/0000775000567000056710000000000012701407231020530 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/templatetags/branding.py0000664000567000056710000000375412701407063022702 0ustar jenkinsjenkins00000000000000# Copyright 2012 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2012 Nebula, Inc. # # 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. """ Template tags for customizing Horizon. """ from django.conf import settings from django.core.urlresolvers import reverse from django import template from django.utils.translation import ugettext_lazy as _ register = template.Library() class SiteBrandingNode(template.Node): def render(self, context): return getattr(settings, "SITE_BRANDING", _("Horizon")) @register.tag def site_branding(parser, token): return SiteBrandingNode() @register.tag def site_title(parser, token): return settings.SITE_BRANDING @register.simple_tag def site_branding_link(): return getattr(settings, "SITE_BRANDING_LINK", reverse("horizon:user_home")) # TODO(jeffjapan): This is just an assignment tag version of the above, replace # when the dashboard is upgraded to a django version that # supports the @assignment_tag decorator syntax instead. class SaveBrandingNode(template.Node): def __init__(self, var_name): self.var_name = var_name def render(self, context): context[self.var_name] = settings.SITE_BRANDING return "" @register.tag def save_site_branding(parser, token): tagname = token.contents.split() return SaveBrandingNode(tagname[-1]) horizon-9.0.0/horizon/templatetags/form_helpers.py0000664000567000056710000000406312701407063023575 0ustar jenkinsjenkins00000000000000# 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. import django.forms from django import template as django_template register = django_template.Library() @register.filter def add_bootstrap_class(field): """Add a "form-control" CSS class to the field's widget. This is so that Bootstrap styles it properly. """ if not isinstance(field.field.widget, ( django.forms.widgets.CheckboxInput, django.forms.widgets.CheckboxSelectMultiple, django.forms.widgets.RadioSelect, django.forms.widgets.FileInput, str, )): field_classes = set(field.field.widget.attrs.get('class', '').split()) field_classes.add('form-control') field.field.widget.attrs['class'] = ' '.join(field_classes) return field @register.filter def is_checkbox(field): return isinstance(field.field.widget, django.forms.CheckboxInput) @register.filter def is_multiple_checkbox(field): return isinstance(field.field.widget, django.forms.CheckboxSelectMultiple) @register.filter def is_radio(field): return isinstance(field.field.widget, django.forms.RadioSelect) @register.filter def is_file(field): return isinstance(field.field.widget, django.forms.FileInput) @register.filter def add_item_url(field): if hasattr(field.field.widget, 'get_add_item_url'): return field.field.widget.get_add_item_url() return None @register.filter def wrapper_classes(field): classes = [] if is_multiple_checkbox(field): classes.append('multiple-checkbox') return ' '.join(classes) horizon-9.0.0/horizon/templatetags/__init__.py0000664000567000056710000000000012701407063022632 0ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/templatetags/bootstrap.py0000664000567000056710000000401612701407063023123 0ustar jenkinsjenkins00000000000000# Copyright 2015 Hewlett Packard Enterprise Software, LLC # All Rights Reserved. # # 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. from __future__ import absolute_import from django import template register = template.Library() @register.inclusion_tag('bootstrap/progress_bar.html') def bs_progress_bar(*args, **kwargs): """A Standard Bootstrap Progress Bar. http://getbootstrap.com/components/#progress param args (Array of Numbers: 0-100): Percent of Progress Bars param context (String): Adds 'progress-bar-{context} to the class attribute param contexts (Array of Strings): Cycles through contexts for stacked bars param text (Boolean): True: shows value within the bar, False: uses sr span param striped (Boolean): Adds 'progress-bar-striped' to the class attribute param animated (Boolean): Adds 'active' to the class attribute if striped param min_val (0): Used for the aria-min value param max_val (0): Used for the aria-max value """ bars = [] contexts = kwargs.get( 'contexts', ['', 'success', 'info', 'warning', 'danger'] ) for ndx, arg in enumerate(args): bars.append( dict(percent=arg, context=kwargs.get('context', contexts[ndx % len(contexts)])) ) return { 'bars': bars, 'text': kwargs.pop('text', False), 'striped': kwargs.pop('striped', False), 'animated': kwargs.pop('animated', False), 'min_val': kwargs.pop('min_val', 0), 'max_val': kwargs.pop('max_val', 100), } horizon-9.0.0/horizon/templatetags/breadcrumb_nav.py0000664000567000056710000000204512701407063024060 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cisco Systems, Inc. # # 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. from django import template register = template.Library() @register.inclusion_tag('horizon/common/_breadcrumb_nav.html', takes_context=True) def breadcrumb_nav(context): return {'actions': context.get('actions'), 'breadcrumb': context.get('custom_breadcrumb'), 'url': context.get('url'), 'page_title': context['page_title'], 'panel': context.request.horizon['panel'], } horizon-9.0.0/horizon/templatetags/shellfilter.py0000664000567000056710000000221112701407063023416 0ustar jenkinsjenkins00000000000000# 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. import django from django import template from django.template import defaultfilters from django.utils import safestring if django.VERSION >= (1, 9): register = template.Library() else: register = template.base.Library() @register.filter(is_safe=True) @defaultfilters.stringfilter def shellfilter(value): """Replace HTML chars for shell usage.""" replacements = {'\\': '\\\\', '`': '\`', "'": "\\'", '"': '\\"'} for search, repl in replacements.items(): value = value.replace(search, repl) return safestring.mark_safe(value) horizon-9.0.0/horizon/templatetags/sizeformat.py0000664000567000056710000000541712701407063023277 0ustar jenkinsjenkins00000000000000# Copyright 2012 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2012 Nebula, Inc. # # 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. """ Template tags for displaying sizes """ from oslo_utils import units from django import template from django.utils import formats from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy register = template.Library() def int_format(value): return int(value) def float_format(value): rounded_value = round(value, 1) if rounded_value.is_integer(): decimal_pos = 0 else: decimal_pos = 1 return formats.number_format(rounded_value, decimal_pos) def filesizeformat(bytes, filesize_number_format): try: bytes = float(bytes) except (TypeError, ValueError, UnicodeDecodeError): return ungettext_lazy("%(size)d Byte", "%(size)d Bytes", 0) % {'size': 0} if bytes < units.Ki: bytes = int(bytes) return ungettext_lazy("%(size)d Byte", "%(size)d Bytes", bytes) % {'size': bytes} if bytes < units.Mi: return _("%s KB") % filesize_number_format(bytes / units.Ki) if bytes < units.Gi: return _("%s MB") % filesize_number_format(bytes / units.Mi) if bytes < units.Ti: return _("%s GB") % filesize_number_format(bytes / units.Gi) if bytes < units.Pi: return _("%s TB") % filesize_number_format(bytes / units.Ti) return _("%s PB") % filesize_number_format(bytes / units.Pi) def float_cast_filesizeformat(value, multiplier=1, format=int_format): try: value = float(value) value = filesizeformat(value * multiplier, format).replace(' ', '') except (TypeError, ValueError): value = value or _('0 Bytes') return value @register.filter(name='mbformat') def mbformat(mb): return float_cast_filesizeformat(mb, units.Mi, int_format) @register.filter(name='mb_float_format') def mb_float_format(mb): return float_cast_filesizeformat(mb, units.Mi, float_format) @register.filter(name='diskgbformat') def diskgbformat(gb): return float_cast_filesizeformat(gb, units.Gi, float_format) horizon-9.0.0/horizon/templatetags/parse_date.py0000664000567000056710000000331212701407063023213 0ustar jenkinsjenkins00000000000000# Copyright 2012 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2012 Nebula, Inc. # # 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. """ Template tags for parsing date strings. """ from datetime import datetime # noqa from django import template from django.utils import timezone register = template.Library() class ParseDateNode(template.Node): def render(self, datestring): """Parses a date-like input string into a timezone aware Python datetime. """ formats = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%d %H:%M:%S.%f", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"] if datestring: for format in formats: try: parsed = datetime.strptime(datestring, format) if not timezone.is_aware(parsed): parsed = timezone.make_aware(parsed, timezone.utc) return parsed except Exception: pass return None @register.filter(name='parse_date') def parse_date(value): return ParseDateNode().render(value) horizon-9.0.0/horizon/templatetags/horizon.py0000664000567000056710000001731212701407063022601 0ustar jenkinsjenkins00000000000000# Copyright 2012 Nebula, Inc. # # 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. from __future__ import absolute_import from collections import OrderedDict from horizon.contrib import bootstrap_datepicker from django.conf import settings from django import template from django.template import Node from django.utils.encoding import force_text from django.utils import translation from django.utils.translation import ugettext_lazy as _ from horizon.base import Horizon # noqa from horizon import conf register = template.Library() class MinifiedNode(Node): def __init__(self, nodelist): self.nodelist = nodelist def render(self, context): return ' '.join( force_text(self.nodelist.render(context).strip()).split() ) @register.filter def has_permissions(user, component): """Checks if the given user meets the permissions requirements for the component. """ return user.has_perms(getattr(component, 'permissions', set())) @register.filter def has_permissions_on_list(components, user): return [component for component in components if has_permissions(user, component)] @register.inclusion_tag('horizon/_sidebar.html', takes_context=True) def horizon_nav(context): if 'request' not in context: return {} current_dashboard = context['request'].horizon.get('dashboard', None) current_panel_group = None current_panel = context['request'].horizon.get('panel', None) dashboards = [] for dash in Horizon.get_dashboards(): panel_groups = dash.get_panel_groups() non_empty_groups = [] for group in panel_groups.values(): allowed_panels = [] for panel in group: if (callable(panel.nav) and panel.nav(context) and panel.can_access(context)): allowed_panels.append(panel) elif (not callable(panel.nav) and panel.nav and panel.can_access(context)): allowed_panels.append(panel) if panel == current_panel: current_panel_group = group.slug if allowed_panels: non_empty_groups.append((group, allowed_panels)) if (callable(dash.nav) and dash.nav(context) and dash.can_access(context)): dashboards.append((dash, OrderedDict(non_empty_groups))) elif (not callable(dash.nav) and dash.nav and dash.can_access(context)): dashboards.append((dash, OrderedDict(non_empty_groups))) return {'components': dashboards, 'user': context['request'].user, 'current': current_dashboard, 'current_panel_group': current_panel_group, 'current_panel': current_panel.slug if current_panel else '', 'request': context['request']} @register.inclusion_tag('horizon/_nav_list.html', takes_context=True) def horizon_main_nav(context): """Generates top-level dashboard navigation entries.""" if 'request' not in context: return {} current_dashboard = context['request'].horizon.get('dashboard', None) dashboards = [] for dash in Horizon.get_dashboards(): if dash.can_access(context): if callable(dash.nav) and dash.nav(context): dashboards.append(dash) elif dash.nav: dashboards.append(dash) return {'components': dashboards, 'user': context['request'].user, 'current': current_dashboard, 'request': context['request']} @register.inclusion_tag('horizon/_subnav_list.html', takes_context=True) def horizon_dashboard_nav(context): """Generates sub-navigation entries for the current dashboard.""" if 'request' not in context: return {} dashboard = context['request'].horizon['dashboard'] panel_groups = dashboard.get_panel_groups() non_empty_groups = [] for group in panel_groups.values(): allowed_panels = [] for panel in group: if (callable(panel.nav) and panel.nav(context) and panel.can_access(context)): allowed_panels.append(panel) elif (not callable(panel.nav) and panel.nav and panel.can_access(context)): allowed_panels.append(panel) if allowed_panels: if group.name is None: non_empty_groups.append((dashboard.name, allowed_panels)) else: non_empty_groups.append((group.name, allowed_panels)) return {'components': OrderedDict(non_empty_groups), 'user': context['request'].user, 'current': context['request'].horizon['panel'].slug, 'request': context['request']} @register.filter def quota(val, units=None): if val == float("inf"): return _("(No Limit)") elif units is not None: return "%s %s %s" % (val, force_text(units), force_text(_("Available"))) else: return "%s %s" % (val, force_text(_("Available"))) @register.filter def quotainf(val, units=None): if val == float("inf"): return '-1' elif units is not None: return "%s %s" % (val, units) else: return val @register.simple_tag def quotapercent(used, limit): if used >= limit or limit == 0: return 100 elif limit == float("inf"): return '[%s, true]' % used else: return round((float(used) / float(limit)) * 100) class JSTemplateNode(template.Node): """Helper node for the ``jstemplate`` template tag.""" def __init__(self, nodelist): self.nodelist = nodelist def render(self, context,): output = self.nodelist.render(context) output = output.replace('[[[', '{{{').replace(']]]', '}}}') output = output.replace('[[', '{{').replace(']]', '}}') output = output.replace('[%', '{%').replace('%]', '%}') return output @register.tag def jstemplate(parser, token): """Replaces ``[[[`` and ``]]]`` with ``{{{`` and ``}}}``, ``[[`` and ``]]`` with ``{{`` and ``}}`` and ``[%`` and ``%]`` with ``{%`` and ``%}`` to avoid conflicts with Django's template engine when using any of the Mustache-based templating libraries. """ nodelist = parser.parse(('endjstemplate',)) parser.delete_first_token() return JSTemplateNode(nodelist) @register.assignment_tag def load_config(): return conf @register.assignment_tag def datepicker_locale(): locale_mapping = getattr(settings, 'DATEPICKER_LOCALES', bootstrap_datepicker.LOCALE_MAPPING) return locale_mapping.get(translation.get_language(), 'en') @register.tag def minifyspace(parser, token): """Removes whitespace including tab and newline characters. Do not use this if you are using a
 tag
    Example usage::
        {% minifyspace %}
            

Foo

{% endminifyspace %} This example would return this HTML::

Foo

""" nodelist = parser.parse(('endminifyspace',)) parser.delete_first_token() return MinifiedNode(nodelist) horizon-9.0.0/horizon/templatetags/truncate_filter.py0000664000567000056710000000203112701407063024273 0ustar jenkinsjenkins00000000000000# Copyright 2012 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2012 Nebula, Inc. # # 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. """ Template tags for truncating strings. """ from django import template register = template.Library() @register.filter("truncate") def truncate(value, size): if len(value) > size and size > 3: return value[0:(size - 3)] + '...' else: return value[0:size] horizon-9.0.0/horizon/locale/0000775000567000056710000000000012701407231017275 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/ru/0000775000567000056710000000000012701407231017723 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/ru/LC_MESSAGES/0000775000567000056710000000000012701407231021510 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/ru/LC_MESSAGES/django.po0000664000567000056710000003152512701407071023322 0ustar jenkinsjenkins00000000000000# Translations template for PROJECT. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # # Translators: # Alexander Vasiliev , 2015 # Ilya Alekseyev , 2015 # Nikita Burtsev, 2015 # Antonio Kless , 2015. #zanata # Ilya Alekseyev , 2015. #zanata # OpenStack Infra , 2015. #zanata # Daisy , 2016. #zanata # Fedor Tarasenko , 2016. #zanata # Filatov Sergey , 2016. #zanata # Grigory Mokhin , 2016. #zanata # Ilya Alekseyev , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-23 05:21+0000\n" "Last-Translator: Ilya Alekseyev \n" "Language: ru\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 3.7.3\n" "Language-Team: Russian\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " Войдите под другим пользователем или вернитеÑÑŒ на домашнюю Ñтраницу\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " ИÑпользовано %(used)s (без ограничений)\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " ИÑпользовано %(used)s из " "%(available)s \n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " ЕÑли вы не уверены какой метод аутентификации выбрать, ÑвÑжитеÑÑŒ Ñ " "вашим ÑиÑтемным админиÑтратором.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d байт" msgstr[1] "%(size)d байт" msgstr[2] "%(size)d байт" #, python-format msgid "%s GB" msgstr "%s ГБ" #, python-format msgid "%s KB" msgstr "%s КБ" #, python-format msgid "%s MB" msgstr "%s МБ" #, python-format msgid "%s PB" msgstr "%s ПБ" #, python-format msgid "%s TB" msgstr "%s ТБ" #, python-format msgid "%s completed successfully." msgstr "%s уÑпешно завершено." #, python-format msgid "%s did not complete." msgstr "%s не завершено." msgid "« Prev" msgstr "« ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰ÐµÐµ" msgid "(No Limit)" msgstr "(без ограничений)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 байт" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "%(resource)s Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼\"%(name)s\" уже ÑущеÑтвует." msgid "Actions" msgstr "ДейÑтвиÑ" msgid "Active Instances:" msgstr "Ðктивные инÑтанÑÑ‹:" msgid "Active RAM:" msgstr "ИÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ RAM:" msgid "Add a row" msgstr "Добавить Ñтроку" msgid "All available" msgstr "Ð’Ñе доÑтупные" msgid "Available" msgstr "ДоÑтупно" msgid "Back" msgstr "Ðазад" #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "Пакетировать Ñлемент" msgstr[1] "Пакетировать Ñлементы" msgstr[2] "Пакетировать Ñлементы" #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "Пакетированный Ñлемент" msgstr[1] "Пакетированные Ñлементы" msgstr[2] "Пакетированные Ñлементы" msgid "Cancel" msgstr "Отмена" msgid "Connect" msgstr "ПодключитьÑÑ" msgid "Delete" msgstr "Удалить" msgid "Deleted" msgstr "Удалено" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Отображен %(content_items)s Ñлемент" msgstr[1] "Отображено %(content_items)s Ñлементов" msgstr[2] "Отображено %(content_items)s Ñлементов" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Отображен %(counter)s Ñлемент" msgstr[1] "Отображено %(counter)s Ñлементов" msgstr[2] "Отображено %(counter)s Ñлементов" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Отображен %(nav_items)s Ñлемент" msgstr[1] "Отображено %(nav_items)s Ñлементов" msgstr[2] "Отображено %(nav_items)s Ñлементов" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "ОпуÑтить Ñлемент" msgstr[1] "ОпуÑтить Ñлементы" msgstr[2] "ОпуÑтить Ñлементы" #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "Опущенный Ñлемент" msgstr[1] "Опущенные Ñлементы" msgstr[2] "Опущенные Ñлементы" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "Ошибка обработки JSON файла Ñообщений '%(path)s': %(exception)s" msgid "Error: " msgstr "Ошибка:" msgid "Fake" msgstr "Fake" msgid "Filter" msgstr "Фильтр" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Ðеправильный формат IP-адреÑа" msgid "Info: " msgstr "ИнформациÑ:" msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "" "ÐÐµÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ метаданных. ИÑпользуйте пары ключ=значение разделенный " "запÑтой." msgid "Invalid subnet mask" msgstr "ÐÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ð¼Ð°Ñка подÑети" msgid "Invalid version for IP address" msgstr "ÐÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ IP-адреÑа" msgid "Limit Summary" msgstr "Сводка лимитов" msgid "Log in" msgstr "Войти" msgid "Login" msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ" msgid "Members" msgstr "УчаÑтники" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "JSON файл Ñообщений '%(path)s' Ñформирован некорректно. %(exception)s" msgid "More Actions" msgstr "Еще дейÑтвиÑ" msgid "Navigation Item" msgstr "Элемент навигации" msgid "Never" msgstr "Ðикогда" msgid "Next" msgstr "Следующий" msgid "Next »" msgstr "Следующее »" msgid "No items to display." msgstr "Ðет Ñлементов Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Ðет Ñовпадений Ð´Ð»Ñ id \"%s\"." msgid "No members." msgstr "Ðет учаÑтников." msgid "None available." msgstr "Ðет доÑтупных." msgid "Not a valid IP protocol number" msgstr "ÐедопуÑтимый номер IP-протокола" msgid "Not a valid port number" msgstr "ÐедопуÑтимый номер порта" msgid "One colon allowed in port range" msgstr "Ð’ ÑпиÑке портов допуÑтима одна запÑтаÑ" msgid "Other" msgstr "Другое" msgid "Password is not accepted" msgstr "Пароль не принÑÑ‚" msgid "Please log in to continue." msgstr "Войдите в ÑиÑтему Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ." msgid "Please select a row before taking that action." msgstr "Выберите Ñтроку перед выполнением Ñтого дейÑтвиÑ." msgid "Processing..." msgstr "Обработка…" msgid "Save" msgstr "Сохранить" #, python-format msgid "Select a %s to browse." msgstr "Выберите %s Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра" msgid "Select a period of time to query its usage:" msgstr "Выберите временной интервал Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа иÑпользованиÑ:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Продать щенка" msgstr[1] "Продать щенков" msgstr[2] "Продать щенков" msgid "Sign In" msgstr "Вход" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Проданый щенок" msgstr[1] "Проданые щенки" msgstr[2] "Проданые щенки" msgid "Submit" msgstr "Отправить" msgid "Success: " msgstr "УÑпешно:" msgid "Summary" msgstr "Итого" #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "Ðтрибут %(attr)s не ÑущеÑтвует в %(obj)s." msgid "The date should be in YYYY-mm-dd format." msgstr "Дата должна иметь формат YYYY-mm-dd." msgid "The string may only contain ASCII printable characters." msgstr "Строка может Ñодержать только печатные ASCII Ñимволы." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "Значение %(resource)s ÑвлÑетÑÑ %(name)s в шаблоне. Ð”Ð»Ñ Ñ„Ð¾Ñ€Ð¼Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñтека из " "Ñтого интерфейÑа значение должно начинатьÑÑ Ñ \"http://\" или \"https://\"" msgid "This Period's GB-Hours:" msgstr "ГБ-чаÑов за период:" msgid "This Period's RAM-Hours:" msgstr "RAM-чаÑов за период:" msgid "This Period's VCPU-Hours:" msgstr "vCPU-чаÑов за период:" msgid "This action cannot be undone." msgstr "Это дейÑтвие не может быть отменено." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Ðевозможно выполнить %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Ð’Ñ‹ не авторизованы. Попробуйте войти в ÑиÑтему еще раз." #, python-format msgid "Unauthorized: %s" msgstr "Ðе авторизован: %s" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "ПоднÑть Ñлемент" msgstr[1] "ПоднÑть Ñлементы" msgstr[2] "ПоднÑть Ñлементы" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "Обновить Ñлемент" msgstr[1] "Обновить Ñлементы" msgstr[2] "Обновить Ñлементы" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "Обновленный Ñлемент" msgstr[1] "Обновленные Ñлементы" msgstr[2] "Обновленные Ñлементы" #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "ПоднÑтый Ñлемент" msgstr[1] "ПоднÑтые Ñлементы" msgstr[2] "ПоднÑтые Ñлементы" msgid "Usage Summary" msgstr "Сводка иÑпользованиÑ" msgid "Warning: " msgstr "Внимание:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Вам не разрешено выполнение: %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Ðет права доÑтупа к %s" msgid "You do not have permission to access the resource:" msgstr "Ð’Ñ‹ не имеете права на доÑтуп к реÑурÑу:" horizon-9.0.0/horizon/locale/ru/LC_MESSAGES/djangojs.po0000664000567000056710000002605112701407063023656 0ustar jenkinsjenkins00000000000000# Translations template for PROJECT. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # # Translators: # Ainur Shakirov , 2015 # Alexander Vasiliev , 2015 # Andreas Jaeger , 2015 # Fedor Tarasenko , 2015 # Ilya Alekseyev , 2015 # Yury Sakarinen, 2014 # Antonio Kless , 2015. #zanata # Ilya Alekseyev , 2015. #zanata # OpenStack Infra , 2015. #zanata # Daisy , 2016. #zanata # Fedor Tarasenko , 2016. #zanata # Filatov Sergey , 2016. #zanata # Ilya Alekseyev , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-16 02:15+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-15 01:07+0000\n" "Last-Translator: Ilya Alekseyev \n" "Language: ru\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 3.7.3\n" "Language-Team: Russian\n" #, python-format msgid "%s GB" msgstr "%s ГБ" #, python-format msgid "%s KB" msgstr "%s кБ" #, python-format msgid "%s MB" msgstr "%s МБ" #, python-format msgid "%s TB" msgstr "%s ТБ" #, python-format msgid "%s bytes" msgstr "%s байт" msgid "(Modified)" msgstr "(Изменено)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 ГБ" msgid "0 MB" msgstr "0 МБ" msgid "Active" msgstr "Ðктивный" msgid "Add Interface" msgstr "Добавить ИнтерфейÑ" msgid "Added" msgstr "Добавлено" msgid "Allocated" msgstr "Выделенный" msgid "An error occurred while updating." msgstr "Во Ð²Ñ€ÐµÐ¼Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð¾ÑˆÐ»Ð° ошибка." msgid "An error occurred. Please try again later." msgstr "Произошла ошибка. Повторите попытку." msgid "Available" msgstr "ДоÑтупно" msgid "Available Metadata" msgstr "ДоÑтупные метаданные" msgid "Back" msgstr "Ðазад" msgid "Cancel" msgstr "Отмена" msgid "Click here for filters." msgstr "Ðажмите здеÑÑŒ Ð´Ð»Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð¾Ð²." msgid "Click here to expand the row and view the errors." msgstr "Ðажмите Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ð° ошибок." msgid "Click to see more details" msgstr "Ðажмите Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñтей" msgid "Click to show or hide" msgstr "Ðажмите Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ð° или ÑкрытиÑ" msgid "Closed" msgstr "Закрыт" msgid "Closing" msgstr "Закрытие" #, python-format msgid "Confirm %s" msgstr "Подтвердите %s" msgid "Confirm Delete Foobars" msgstr "Подтвердите удаление Foobars" msgid "Connecting" msgstr "Подключение" msgid "Could not decrypt the password" msgstr "Ðе удалоÑÑŒ раÑшифровать пароль" msgid "Could not read the file" msgstr "Ðе удалоÑÑŒ прочитать файл" msgid "Create Subnet" msgstr "Создать подÑеть" msgid "Current Usage" msgstr "ИÑпользовано на текущий момент" msgid "Custom" msgstr "Выборочный" msgid "Customization Script" msgstr "Скрипт наÑтройки" msgid "Danger" msgstr "ОпаÑноÑть" msgid "Danger: " msgstr "ОпаÑноÑть:" msgid "Decimal required" msgstr "ТребуетÑÑ Ð´ÐµÑÑтичное чиÑло" msgid "Delete" msgstr "Удалить" msgid "Delete Instance" msgstr "Удалить инÑтанÑ" msgid "Delete Interface" msgstr "Удалить интерфейÑ" msgid "Delete Network" msgstr "Удалить Ñеть" msgid "Delete Router" msgstr "Удалить маршрутизатор" msgid "Delete Subnet" msgstr "Удалить подÑеть" #, python-format msgid "Deleted : %s." msgstr "Удалено: %s." msgid "Detail Information" msgstr "ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "Отображение %(count)s из %(total)s Ñлементов" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "Отображено %s значение" msgstr[1] "Отображено %s значений" msgstr[2] "Отображено %s значений" msgid "Duplicate keys are not allowed" msgstr "ПовторÑющиеÑÑ ÐºÐ»ÑŽÑ‡Ð¸ не допуÑкаютÑÑ" msgid "Error" msgstr "Ошибка" msgid "Error: " msgstr "Ошибка:" msgid "Example" msgstr "Пример" msgid "Existing Metadata" msgstr "ИмеющиеÑÑ Ð¼ÐµÑ‚Ð°Ð´Ð°Ð½Ð½Ñ‹Ðµ" msgid "Expand to see allocated items" msgstr "Показать выделенные Ñлементы" msgid "Expand to see available items" msgstr "Показать вÑе доÑтупные Ñлементы" msgid "Filter" msgstr "Фильтр" msgid "Finish" msgstr "Закончить" msgid "Flavor" msgstr "Тип инÑтанÑа" msgid "Full Text Search" msgstr "ПолнотекÑтовый поиÑк" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "IP адреÑ" msgid "Info" msgstr "ИнформациÑ" msgid "Integer required" msgstr "ТребуетÑÑ Ñ†ÐµÐ»Ð¾Ðµ чиÑло" msgid "Interfaces" msgstr "ИнтерфейÑÑ‹" msgid "Load script from a file" msgstr "Загрузить Ñкрипт из файла" msgid "Loading" msgstr "Загрузка" msgid "Max" msgstr "МакÑ." msgid "Max length" msgstr "МакÑ. длина" msgid "Min" msgstr "Мин." msgid "Min length" msgstr "Мин. длина" msgid "Name" msgstr "Ðазвание" msgid "Next" msgstr "Вперёд" msgid "No" msgstr "Ðет" msgid "No Limit" msgstr "Ðет ограничений" msgid "No available items" msgstr "Ðет доÑтупных Ñлементов" msgid "No available metadata" msgstr "Метаданные недоÑтупны" msgid "No data available." msgstr "Ðет данных." msgid "No description available." msgstr "ОпиÑание недоÑтупно." msgid "No existing metadata" msgstr "Ðет метаданных" msgid "No items to display." msgstr "Ðет Ñлементов Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ." msgid "No roles" msgstr "Ðет ролей" msgid "None" msgstr "Ðет" msgid "Not authorized to do this operation." msgstr "Ðет прав Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ." msgid "Notice: " msgstr "Примечание:" msgid "Open" msgstr "Открыт" msgid "Open Console" msgstr "Открыть конÑоль" msgid "Passwords do not match." msgstr "Пароли не Ñовпадают." msgid "Pattern mismatch" msgstr "ÐеÑоответÑтвие шаблону" msgid "Please confirm your selection. " msgstr "Подтвердите Ñвой выбор." msgid "Prompt" msgstr "ПодÑказка" msgid "Re-order items using drag and drop" msgstr "Переопределите Ñлементы перемещением" msgid "Remaining" msgstr "Свободно" msgid "Remove" msgstr "Удалить" msgid "Required" msgstr "ТребуетÑÑ" msgid "Roles" msgstr "Роли" msgid "STATUS" msgstr "СОСТОЯÐИЕ" #. Strings between {$ and $} should be left untranslated. msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgstr "" "Размер Ñкрипта: {$ (scriptLength || 0) | bytes $} из {$ config." "MAX_SCRIPT_SIZE | bytes $}" msgid "Search in current results" msgstr "ПоиÑк в текущих результатах" msgid "Select an item from Available items below" msgstr "Выберите Ñлемент из доÑтупных Ñлементов ниже" msgid "Select one" msgstr "Выберите один" msgid "Server Name" msgstr "Ð˜Ð¼Ñ Ñервера" msgid "Shutdown" msgstr "Выключение" msgid "Status" msgstr "СтатуÑ" #, python-format msgid "Status: %s" msgstr "СтатуÑ: %s" msgid "Submit" msgstr "Отправить" msgid "Subnets" msgstr "ПодÑети" msgid "Success" msgstr "УÑпешно" msgid "Success: " msgstr "УÑпешно:" msgid "Text" msgstr "ТекÑÑ‚" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "ДейÑтвие не может быть выполнено. Эта Ñтрока Ñодержит ошибки или " "недоÑтаточную информацию. " msgid "The script is larger than the maximum size" msgstr "Размер Ñкрипта больше макÑимально допуÑтимого размера" msgid "There was a problem communicating with the server, please try again." msgstr "Обнаружена проблема при Ñоединении Ñ Ñервером. Повторите попытку." msgid "There was an error submitting the form. Please try again." msgstr "При отправке формы произошла ошибка. Повторите попытку." msgid "Toggle Dropdown" msgstr "открыть/ÑпрÑтать выпадающий ÑпиÑок" msgid "Toggle navigation" msgstr "Переключить навигацию" msgid "Total" msgstr "Ð’Ñего" #, python-format msgid "Unable to delete: %s." msgstr "Ðевозможно удалить: %s." msgid "Unlimited" msgstr "Ðеограничено" msgid "View Details" msgstr "ПроÑмотреть детали" msgid "View Instance Details" msgstr "ПоÑмотреть информацию об инÑтанÑе." msgid "View Router Details" msgstr "ПоÑмотреть информацию о маршрутизаторе" msgid "Warning" msgstr "Предупреждение" msgid "Warning: " msgstr "Внимание:" msgid "Working" msgstr "Обработка" msgid "Yes" msgstr "Да" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "Ð’Ñ‹ можете указать метаданные реÑурÑа Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰Ð°Ñ Ñлементы из левого Ñтолбца в " "правый. Ð’ левом Ñтолбце имеютÑÑ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¼ÐµÑ‚Ð°Ð´Ð°Ð½Ð½Ñ‹Ñ… из каталога " "метаданных Glance. ИÑпользуйте опцию \"Другой\" Ð´Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… " "вами ключей" #, python-format msgid "You have selected %s. " msgstr "Выбрано %s." msgid "description" msgstr "опиÑание" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "пул по умолчанию" #, python-format msgid "selected \"%s\"" msgstr "выбрано \"%s\"" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || 'Ðет Ñлементов Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ.' $}" horizon-9.0.0/horizon/locale/en_GB/0000775000567000056710000000000012701407231020247 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/en_GB/LC_MESSAGES/0000775000567000056710000000000012701407231022034 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/en_GB/LC_MESSAGES/django.po0000664000567000056710000002233212701407063023643 0ustar jenkinsjenkins00000000000000# Andi Chandler , 2015. #zanata # OpenStack Infra , 2015. #zanata # Rob Cresswell , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-02-10 12:12+0000\n" "Last-Translator: Rob Cresswell \n" "Language-Team: English (United Kingdom)\n" "Language: en-GB\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " Login as different user or go back to home page\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " Used %(used)s (No Limit)\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " Used %(used)s of %(available)s \n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Byte" msgstr[1] "%(size)d Bytes" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s completed successfully." #, python-format msgid "%s did not complete." msgstr "%s did not complete." msgid "« Prev" msgstr "« Prev" msgid "(No Limit)" msgstr "(No Limit)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 Bytes" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "A %(resource)s with the name \"%(name)s\" already exists." msgid "Actions" msgstr "Actions" msgid "Active Instances:" msgstr "Active Instances:" msgid "Active RAM:" msgstr "Active RAM:" msgid "Add a row" msgstr "Add a row" msgid "All available" msgstr "All available" msgid "Available" msgstr "Available" msgid "Back" msgstr "Back" #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "Batch Item" msgstr[1] "Batch Items" #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "Batched Item" msgstr[1] "Batched Items" msgid "Cancel" msgstr "Cancel" msgid "Connect" msgstr "Connect" msgid "Delete" msgstr "Delete" msgid "Deleted" msgstr "Deleted" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Displaying %(content_items)s item" msgstr[1] "Displaying %(content_items)s items" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Displaying %(counter)s item" msgstr[1] "Displaying %(counter)s items" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Displaying %(nav_items)s item" msgstr[1] "Displaying %(nav_items)s items" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "Down Item" msgstr[1] "Down Items" #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "Downed Item" msgstr[1] "Downed Items" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "Error processing message json file '%(path)s': %(exception)s" msgid "Error: " msgstr "Error: " msgid "Fake" msgstr "Fake" msgid "Filter" msgstr "Filter" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Incorrect format for IP address" msgid "Info: " msgstr "Info: " msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "Invalid metadata entry. Use comma-separated key=value pairs" msgid "Invalid subnet mask" msgstr "Invalid subnet mask" msgid "Invalid version for IP address" msgstr "Invalid version for IP address" msgid "Limit Summary" msgstr "Limit Summary" msgid "Log in" msgstr "Log in" msgid "Login" msgstr "Login" msgid "Members" msgstr "Members" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "Message json file '%(path)s' is malformed. %(exception)s" msgid "More Actions" msgstr "More Actions" msgid "Navigation Item" msgstr "Navigation Item" msgid "Never" msgstr "Never" msgid "Next" msgstr "Next" msgid "Next »" msgstr "Next »" msgid "No items to display." msgstr "No items to display." #, python-format msgid "No match returned for the id \"%s\"." msgstr "No match returned for the id \"%s\"." msgid "No members." msgstr "No members." msgid "None available." msgstr "None available." msgid "Not a valid IP protocol number" msgstr "Not a valid IP protocol number" msgid "Not a valid port number" msgstr "Not a valid port number" msgid "One colon allowed in port range" msgstr "One colon allowed in port range" msgid "Other" msgstr "Other" msgid "Password is not accepted" msgstr "Password is not accepted" msgid "Please log in to continue." msgstr "Please log in to continue." msgid "Please select a row before taking that action." msgstr "Please select a row before taking that action." msgid "Processing..." msgstr "Processing..." msgid "Save" msgstr "Save" #, python-format msgid "Select a %s to browse." msgstr "Select a %s to browse." msgid "Select a period of time to query its usage:" msgstr "Select a period of time to query its usage:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Sell Puppy" msgstr[1] "Sell Puppies" msgid "Sign In" msgstr "Sign In" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Sold Puppy" msgstr[1] "Sold Puppies" msgid "Submit" msgstr "Submit" msgid "Success: " msgstr "Success: " msgid "Summary" msgstr "Summary" msgid "The date should be in YYYY-mm-dd format." msgstr "The date should be in YYYY-mm-dd format." msgid "The string may only contain ASCII printable characters." msgstr "The string may only contain ASCII printable characters." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgid "This Period's GB-Hours:" msgstr "This Period's GB-Hours:" msgid "This Period's RAM-Hours:" msgstr "This Period's RAM-Hours:" msgid "This Period's VCPU-Hours:" msgstr "This Period's VCPU-Hours:" msgid "This action cannot be undone." msgstr "This action cannot be undone." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Unable to %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Unauthorised. Please try logging in again." #, python-format msgid "Unauthorized: %s" msgstr "Unauthorised: %s" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "Up Item" msgstr[1] "Up Items" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "Update Item" msgstr[1] "Update Items" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "Updated Item" msgstr[1] "Updated Items" #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "Upped Item" msgstr[1] "Upped Items" msgid "Usage Summary" msgstr "Usage Summary" msgid "Warning: " msgstr "Warning: " #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "You are not allowed to %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "You are not authorised to access %s" msgid "You do not have permission to access the resource:" msgstr "You do not have permission to access the resource:" horizon-9.0.0/horizon/locale/en_GB/LC_MESSAGES/djangojs.po0000664000567000056710000001704212701407063024202 0ustar jenkinsjenkins00000000000000# Andi Chandler , 2015. #zanata # OpenStack Infra , 2015. #zanata # Rob Cresswell , 2015. #zanata # Rob Cresswell , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-02-10 12:11+0000\n" "Last-Translator: Rob Cresswell \n" "Language-Team: English (United Kingdom)\n" "Language: en-GB\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s bytes" msgid "(Modified)" msgstr "(Modified)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "Active" msgid "Add Interface" msgstr "Add Interface" msgid "Added" msgstr "Added" msgid "Allocated" msgstr "Allocated" msgid "An error occurred while updating." msgstr "An error occurred while updating." msgid "An error occurred. Please try again later." msgstr "An error occurred. Please try again later." msgid "Available" msgstr "Available" msgid "Available Metadata" msgstr "Available Metadata" msgid "Back" msgstr "Back" msgid "Cancel" msgstr "Cancel" msgid "Click here for filters." msgstr "Click here for filters." msgid "Click here to expand the row and view the errors." msgstr "Click here to expand the row and view the errors." msgid "Click to see more details" msgstr "Click to see more details" msgid "Click to show or hide" msgstr "Click to show or hide" msgid "Closed" msgstr "Closed" msgid "Closing" msgstr "Closing" #, python-format msgid "Confirm %s" msgstr "Confirm %s" msgid "Confirm Delete Foobars" msgstr "Confirm Delete Foobars" msgid "Connecting" msgstr "Connecting" msgid "Could not decrypt the password" msgstr "Could not decrypt the password" msgid "Could not read the file" msgstr "Could not read the file" msgid "Create Subnet" msgstr "Create Subnet" msgid "Current Usage" msgstr "Current Usage" msgid "Custom" msgstr "Custom" msgid "Customization Script" msgstr "Customisation Script" msgid "Danger" msgstr "Danger" msgid "Danger: " msgstr "Danger: " msgid "Decimal required" msgstr "Decimal required" msgid "Delete" msgstr "Delete" msgid "Delete Instance" msgstr "Delete Instance" msgid "Delete Interface" msgstr "Delete Interface" msgid "Delete Network" msgstr "Delete Network" msgid "Delete Router" msgstr "Delete Router" msgid "Delete Subnet" msgstr "Delete Subnet" #, python-format msgid "Deleted : %s." msgstr "Deleted : %s." msgid "Detail Information" msgstr "Detail Information" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "Displaying %(count)s of %(total)s items" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "Displaying %s item" msgstr[1] "Displaying %s items" msgid "Duplicate keys are not allowed" msgstr "Duplicate keys are not allowed" msgid "Error" msgstr "Error" msgid "Error: " msgstr "Error: " msgid "Existing Metadata" msgstr "Existing Metadata" msgid "Expand to see allocated items" msgstr "Expand to see allocated items" msgid "Expand to see available items" msgstr "Expand to see available items" msgid "Filter" msgstr "Filter" msgid "Finish" msgstr "Finish" msgid "Flavor" msgstr "Flavour" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "IP Addresses" msgid "Info" msgstr "Info" msgid "Integer required" msgstr "Integer required" msgid "Interfaces" msgstr "Interfaces" msgid "Load script from a file" msgstr "Load script from a file" msgid "Loading" msgstr "Loading" msgid "Max" msgstr "Max" msgid "Max length" msgstr "Max length" msgid "Min" msgstr "Min" msgid "Min length" msgstr "Min length" msgid "Name" msgstr "Name" msgid "Next" msgstr "Next" msgid "No" msgstr "No" msgid "No Limit" msgstr "No Limit" msgid "No available items" msgstr "No available items" msgid "No available metadata" msgstr "No available metadata" msgid "No data available." msgstr "No data available." msgid "No description available." msgstr "No description available." msgid "No existing metadata" msgstr "No existing metadata" msgid "No items to display." msgstr "No items to display." msgid "No roles" msgstr "No roles" msgid "None" msgstr "None" msgid "Not authorized to do this operation." msgstr "Not authorised to do this operation." msgid "Notice: " msgstr "Notice: " msgid "Open" msgstr "Open" msgid "Open Console" msgstr "Open Console" msgid "Passwords do not match." msgstr "Passwords do not match." msgid "Pattern mismatch" msgstr "Pattern mismatch" msgid "Please confirm your selection. " msgstr "Please confirm your selection. " msgid "Prompt" msgstr "Prompt" msgid "Re-order items using drag and drop" msgstr "Re-order items using drag and drop" msgid "Remaining" msgstr "Remaining" msgid "Remove" msgstr "Remove" msgid "Required" msgstr "Required" msgid "Roles" msgstr "Roles" msgid "STATUS" msgstr "STATUS" msgid "Select an item from Available items below" msgstr "Select an item from Available items below" msgid "Select one" msgstr "Select one" msgid "Server Name" msgstr "Server Name" msgid "Shutdown" msgstr "Shutdown" msgid "Status" msgstr "Status" #, python-format msgid "Status: %s" msgstr "Status: %s" msgid "Submit" msgstr "Submit" msgid "Subnets" msgstr "Subnets" msgid "Success" msgstr "Success" msgid "Success: " msgstr "Success: " msgid "Text" msgstr "Text" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgid "There was a problem communicating with the server, please try again." msgstr "There was a problem communicating with the server, please try again." msgid "There was an error submitting the form. Please try again." msgstr "There was an error submitting the form. Please try again." msgid "Toggle Dropdown" msgstr "Toggle Dropdown" msgid "Total" msgstr "Total" #, python-format msgid "Unable to delete: %s." msgstr "Unable to delete: %s." msgid "View Details" msgstr "View Details" msgid "View Instance Details" msgstr "View Instance Details" msgid "View Router Details" msgstr "View Router Details" msgid "Warning" msgstr "Warning" msgid "Warning: " msgstr "Warning: " msgid "Working" msgstr "Working" msgid "Yes" msgstr "Yes" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." #, python-format msgid "You have selected %s. " msgstr "You have selected %s. " msgid "description" msgstr "description" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "pool default" #, python-format msgid "selected \"%s\"" msgstr "selected \"%s\"" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || 'No items to display.' $}" horizon-9.0.0/horizon/locale/hu/0000775000567000056710000000000012701407231017711 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/hu/LC_MESSAGES/0000775000567000056710000000000012701407231021476 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/hu/LC_MESSAGES/django.po0000664000567000056710000000717012701407063023310 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Hungarian\n" "Language: hu\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s sikeresen befejezÅ‘dött." #, python-format msgid "%s did not complete." msgstr "%s nem fejezÅ‘dött be." msgid "Actions" msgstr "Műveletek" msgid "Add a row" msgstr "Sor hozzáadása" msgid "All available" msgstr "Összes elérhetÅ‘" msgid "Available" msgstr "ElérhetÅ‘" msgid "Back" msgstr "Vissza" msgid "Cancel" msgstr "Mégse" msgid "Delete" msgstr "Törlés" msgid "Deleted" msgstr "Törölve" msgid "Error: " msgstr "Hiba: " msgid "Filter" msgstr "SzűrÅ‘" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Helytelen IP-cím formátum" msgid "Info: " msgstr "Információ: " msgid "Invalid subnet mask" msgstr "Érvénytelen alhálózati maszk" msgid "Invalid version for IP address" msgstr "Érvénytelen IP-cím változat" msgid "Login" msgstr "Bejelentkezés" msgid "Members" msgstr "Tagok" msgid "Navigation Item" msgstr "Navigációs elem" msgid "Next" msgstr "Tovább" msgid "No items to display." msgstr "Nincs megjeleníthetÅ‘ elem." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Nincs találat a(z) „%s†azonosítóhoz." msgid "No members." msgstr "Nincsenek tagok." msgid "None available." msgstr "Nincs elérhetÅ‘." msgid "Not a valid IP protocol number" msgstr "Nem érvényes IP protokoll szám" msgid "Not a valid port number" msgstr "Nem érvényes port szám" msgid "Other" msgstr "Egyéb" msgid "Password is not accepted" msgstr "A jelszó nincs elfogadva" msgid "Please log in to continue." msgstr "Lépjen be a folytatáshoz." msgid "Please select a row before taking that action." msgstr "Válasszon ki egy sort a művelet végrehajtása elÅ‘tt." msgid "Processing..." msgstr "Feldolgozás…" msgid "Save" msgstr "Mentés" #, python-format msgid "Select a %s to browse." msgstr "A böngészéshez válasszon egy %s elemet" msgid "Sign In" msgstr "Bejelentkezés" msgid "Submit" msgstr "Beküldés" msgid "Success: " msgstr "Siker: " msgid "Summary" msgstr "Összegzés" msgid "The date should be in YYYY-mm-dd format." msgstr "A dátum formátuma ÉÉÉÉ-hh-nn." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Nem hajtható végre: %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Jogosulatlan hozzáférés. Próbáljon meg belépni újra." msgid "Usage Summary" msgstr "Használati összegzés" msgid "Warning: " msgstr "Figyelmeztetés: " #, python-format msgid "You are not authorized to access %s" msgstr "Nincsen engedélyezve a hozzáférés ehhez: %s" msgid "You do not have permission to access the resource:" msgstr "Nincs jogosultsága a forrás eléréséhez:" horizon-9.0.0/horizon/locale/fil/0000775000567000056710000000000012701407231020047 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/fil/LC_MESSAGES/0000775000567000056710000000000012701407231021634 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/fil/LC_MESSAGES/django.po0000664000567000056710000001002512701407063023437 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Filipino\n" "Language: fil\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=n > 1\n" #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s ay tagumpay na nakumpleto." #, python-format msgid "%s did not complete." msgstr "%s ay hindi nakumpleto." msgid "« Prev" msgstr "« Nakaraan" msgid "Actions" msgstr "Mga Aksyon" msgid "Add a row" msgstr "Magdagdag ng hilera" msgid "All available" msgstr "Lahat magagamit" msgid "Available" msgstr "Magagamit" msgid "Back" msgstr "Balik" msgid "Cancel" msgstr "Ikansela" msgid "Delete" msgstr "Alisin" msgid "Deleted" msgstr "Naalis na" msgid "Error: " msgstr "May Pagkakamali:" msgid "Filter" msgstr "Salain" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Maling format para sa mga IP address" msgid "Info: " msgstr "Info:" msgid "Invalid subnet mask" msgstr "Maling subnet mask" msgid "Invalid version for IP address" msgstr "Maling bersyon para sa IP address" msgid "Limit Summary" msgstr "Buod ng Limitasyon" msgid "Login" msgstr "Login" msgid "Members" msgstr "Mga Miyembro" msgid "Navigation Item" msgstr "Navigation Item" msgid "Never" msgstr "Hindi kailanman" msgid "Next" msgstr "Susunod" msgid "No items to display." msgstr "Walang mga items na ipapakita." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Walang tugma para sa id na \"%s\"." msgid "No members." msgstr "Walang miyembro." msgid "None available." msgstr "Walang magagamit" msgid "Not a valid IP protocol number" msgstr "Hindi wasto ang numero ng IP protocol" msgid "Not a valid port number" msgstr "Hindi wasto ang numero ng port" msgid "One colon allowed in port range" msgstr "Isang tutuldok o \"colon\" ang pinapayagan sa port range" msgid "Other" msgstr "Iba" msgid "Password is not accepted" msgstr "Ang Password ay hindi tinanggap" msgid "Please log in to continue." msgstr "Mangyaring mag-log in upang magpatuloy." msgid "Please select a row before taking that action." msgstr "Mangyaring pumili ng isang hilera bago simulan ang aksiyon." msgid "Processing..." msgstr "Nagpoproseso..." msgid "Save" msgstr "I-Save" #, python-format msgid "Select a %s to browse." msgstr "Pumili ng %s upang mag browse." msgid "Sign In" msgstr "Mag Sign In" msgid "Submit" msgstr "Submit" msgid "Success: " msgstr "Tagumpay:" msgid "Summary" msgstr "Buod" msgid "The date should be in YYYY-mm-dd format." msgstr "Ang petsa ay dapat nasa YYYY-mm-dd format." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Hindi magawa ang %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Di-awtorisado. Mangyaring subukang mag-log in muli." #, python-format msgid "Unauthorized: %s" msgstr "Di-awtorisado: %s" msgid "Usage Summary" msgstr "Buod ng Paggamit" msgid "Warning: " msgstr "Babala:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Wala kang pahintulot sa %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Wala kang pahintulot upang ma-access ang %s" msgid "You do not have permission to access the resource:" msgstr "Wala kang pahintulot upang ma-access ang pagkukunan:" horizon-9.0.0/horizon/locale/pt/0000775000567000056710000000000012701407231017720 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/pt/LC_MESSAGES/0000775000567000056710000000000012701407231021505 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/pt/LC_MESSAGES/django.po0000664000567000056710000001355012701407063023316 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Portuguese\n" "Language: pt\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " Se não tiver certeza sobre que método de autenticação usar, contacte o " "seu administrador.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s concluída com êxito." #, python-format msgid "%s did not complete." msgstr "%s não foi concluída." msgid "« Prev" msgstr "« Anterior" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 bytes" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Já existe um %(resource)s com o nome \"%(name)s\"." msgid "Actions" msgstr "Ações" msgid "Active Instances:" msgstr "Casos Ativos:" msgid "Active RAM:" msgstr "RAM Ativa:" msgid "Add a row" msgstr "Adicione uma fila" msgid "All available" msgstr "Todos disponíveis." msgid "Available" msgstr "Disponível" msgid "Back" msgstr "Voltar" msgid "Cancel" msgstr "Cancelar" msgid "Connect" msgstr "Conectar-se" msgid "Delete" msgstr "Apagar" msgid "Deleted" msgstr "Apgado" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Visualizar %(counter)s item" msgstr[1] "Visualizar %(counter)s itens" msgid "Error: " msgstr "Erro: " msgid "Fake" msgstr "Falso" msgid "Filter" msgstr "Filtro" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Formato incorreto para o endereço de IP" msgid "Info: " msgstr "Informação: " msgid "Invalid subnet mask" msgstr "Máscara de sub-rede inválida" msgid "Invalid version for IP address" msgstr "Versão inválido para o endereço de IP" msgid "Limit Summary" msgstr "Resumo do Limite" msgid "Login" msgstr "Iniciar Sessão" msgid "Members" msgstr "Membros." msgid "More Actions" msgstr "Mais Ações" msgid "Navigation Item" msgstr "Item de Navegação" msgid "Never" msgstr "Nunca" msgid "Next" msgstr "Seguinte" msgid "Next »" msgstr "Seguinte »" msgid "No items to display." msgstr "Não há itens para exibir." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Não foi encontrada correspondência para a id. \"%s\"." msgid "No members." msgstr "Sem membros." msgid "None available." msgstr "Nenhum disponível." msgid "Not a valid IP protocol number" msgstr "Não é um número de protocolo de IP válido" msgid "Not a valid port number" msgstr "Não é um número de porta válido" msgid "One colon allowed in port range" msgstr "São permitidos dois pontos no limite da porta" msgid "Other" msgstr "Outros" msgid "Password is not accepted" msgstr "A senha não foi aceite" msgid "Please log in to continue." msgstr "Por favor, inicie a sessão para continuar." msgid "Please select a row before taking that action." msgstr "Por favor, selecione uma linha antes de realizar esta ação." msgid "Processing..." msgstr "A processar ..." msgid "Save" msgstr "Guardar" #, python-format msgid "Select a %s to browse." msgstr "Selecione um %s para explorar." msgid "Select a period of time to query its usage:" msgstr "Selecione um período de tempo para consultar a sua utilização:" msgid "Sign In" msgstr "Registar" msgid "Submit" msgstr "Submeter" msgid "Success: " msgstr "Sucesso: " msgid "Summary" msgstr "Resumo" msgid "The date should be in YYYY-mm-dd format." msgstr "A data deverá estar no formato YYYY-mm-dd." msgid "The string may only contain ASCII printable characters." msgstr "Esta string só pode conter caracteres ASCII imprimíveis." msgid "This Period's GB-Hours:" msgstr "Horas-GB deste Período:" msgid "This Period's RAM-Hours:" msgstr "Horas-RAM deste Período:" msgid "This Period's VCPU-Hours:" msgstr "Horas-VCPU deste Período:" msgid "This action cannot be undone." msgstr "Esta acão não pode ser desfeita." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Não é possível %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Não autorizado. Por favor, tente iniciar a sessão novamente." #, python-format msgid "Unauthorized: %s" msgstr "Não autorizado: %s" msgid "Usage Summary" msgstr "Resumo da Utilização" msgid "Warning: " msgstr "Aviso: " #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Não está autorizado para %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Não está autorizado a aceder %s" msgid "You do not have permission to access the resource:" msgstr "Não tem autorização para aceder ao recurso:" horizon-9.0.0/horizon/locale/en_AU/0000775000567000056710000000000012701407231020264 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/en_AU/LC_MESSAGES/0000775000567000056710000000000012701407231022051 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/en_AU/LC_MESSAGES/django.po0000664000567000056710000002247712701407071023671 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata # Tom Fifield , 2015. #zanata # Tom Fifield , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-22 09:13+0000\n" "Last-Translator: Tom Fifield \n" "Language-Team: English (Australia)\n" "Language: en-AU\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " Login as different user or go back to home page\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " Used %(used)s (No Limit)\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " Used %(used)s of %(available)s \n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Byte" msgstr[1] "%(size)d Bytes" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s completed successfully." #, python-format msgid "%s did not complete." msgstr "%s did not complete." msgid "« Prev" msgstr "« Prev" msgid "(No Limit)" msgstr "(No Limit)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 Bytes" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "A %(resource)s with the name \"%(name)s\" already exists." msgid "Actions" msgstr "Actions" msgid "Active Instances:" msgstr "Active Instances:" msgid "Active RAM:" msgstr "Active RAM:" msgid "Add a row" msgstr "Add a row" msgid "All available" msgstr "All available" msgid "Available" msgstr "Available" msgid "Back" msgstr "Back" #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "Batch Item" msgstr[1] "Batch Items" #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "Batched Item" msgstr[1] "Batched Items" msgid "Cancel" msgstr "Cancel" msgid "Connect" msgstr "Connect" msgid "Delete" msgstr "Delete" msgid "Deleted" msgstr "Deleted" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Displaying %(content_items)s item" msgstr[1] "Displaying %(content_items)s items" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Displaying %(counter)s item" msgstr[1] "Displaying %(counter)s items" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Displaying %(nav_items)s item" msgstr[1] "Displaying %(nav_items)s items" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "Down Item" msgstr[1] "Down Items" #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "Downed Item" msgstr[1] "Downed Items" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "Error processing message json file '%(path)s': %(exception)s" msgid "Error: " msgstr "Error: " msgid "Fake" msgstr "Fake" msgid "Filter" msgstr "Filter" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Incorrect format for IP address" msgid "Info: " msgstr "Info: " msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "Invalid metadata entry. Use comma-separated key=value pairs" msgid "Invalid subnet mask" msgstr "Invalid subnet mask" msgid "Invalid version for IP address" msgstr "Invalid version for IP address" msgid "Limit Summary" msgstr "Limit Summary" msgid "Log in" msgstr "Log in" msgid "Login" msgstr "Login" msgid "Members" msgstr "Members" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "Message json file '%(path)s' is malformed. %(exception)s" msgid "More Actions" msgstr "More Actions" msgid "Navigation Item" msgstr "Navigation Item" msgid "Never" msgstr "Never" msgid "Next" msgstr "Next" msgid "Next »" msgstr "Next »" msgid "No items to display." msgstr "No items to display." #, python-format msgid "No match returned for the id \"%s\"." msgstr "No match returned for the id \"%s\"." msgid "No members." msgstr "No members." msgid "None available." msgstr "None available." msgid "Not a valid IP protocol number" msgstr "Not a valid IP protocol number" msgid "Not a valid port number" msgstr "Not a valid port number" msgid "One colon allowed in port range" msgstr "One colon allowed in port range" msgid "Other" msgstr "Other" msgid "Password is not accepted" msgstr "Password is not accepted" msgid "Please log in to continue." msgstr "Please log in to continue." msgid "Please select a row before taking that action." msgstr "Please select a row before taking that action." msgid "Processing..." msgstr "Processing..." msgid "Save" msgstr "Save" #, python-format msgid "Select a %s to browse." msgstr "Select a %s to browse." msgid "Select a period of time to query its usage:" msgstr "Select a period of time to query its usage:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Sell Puppy" msgstr[1] "Sell Puppies" msgid "Sign In" msgstr "Sign In" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Sold Puppy" msgstr[1] "Sold Puppies" msgid "Submit" msgstr "Submit" msgid "Success: " msgstr "Success: " msgid "Summary" msgstr "Summary" #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "The attribute %(attr)s doesn't exist on %(obj)s." msgid "The date should be in YYYY-mm-dd format." msgstr "The date should be in YYYY-mm-dd format." msgid "The string may only contain ASCII printable characters." msgstr "The string may only contain ASCII printable characters." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgid "This Period's GB-Hours:" msgstr "This Period's GB-Hours:" msgid "This Period's RAM-Hours:" msgstr "This Period's RAM-Hours:" msgid "This Period's VCPU-Hours:" msgstr "This Period's VCPU-Hours:" msgid "This action cannot be undone." msgstr "This action cannot be undone." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Unable to %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Unauthorised. Please try logging in again." #, python-format msgid "Unauthorized: %s" msgstr "Unauthorised: %s" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "Up Item" msgstr[1] "Up Items" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "Update Item" msgstr[1] "Update Items" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "Updated Item" msgstr[1] "Updated Items" #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "Upped Item" msgstr[1] "Upped Items" msgid "Usage Summary" msgstr "Usage Summary" msgid "Warning: " msgstr "Warning: " #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "You are not allowed to %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "You are not authorised to access %s" msgid "You do not have permission to access the resource:" msgstr "You do not have permission to access the resource:" horizon-9.0.0/horizon/locale/en_AU/LC_MESSAGES/djangojs.po0000664000567000056710000002005212701407071024211 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata # Tom Fifield , 2015. #zanata # Tom Fifield , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-22 09:13+0000\n" "Last-Translator: Tom Fifield \n" "Language-Team: English (Australia)\n" "Language: en-AU\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s bytes" msgid "(Modified)" msgstr "(Modified)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "Active" msgid "Add Interface" msgstr "Add Interface" msgid "Added" msgstr "Added" msgid "Allocated" msgstr "Allocated" msgid "An error occurred while updating." msgstr "An error occurred while updating." msgid "An error occurred. Please try again later." msgstr "An error occurred. Please try again later." msgid "Available" msgstr "Available" msgid "Available Metadata" msgstr "Available Metadata" msgid "Back" msgstr "Back" msgid "Cancel" msgstr "Cancel" msgid "Click here for filters." msgstr "Click here for filters." msgid "Click here to expand the row and view the errors." msgstr "Click here to expand the row and view the errors." msgid "Click to see more details" msgstr "Click to see more details" msgid "Click to show or hide" msgstr "Click to show or hide" msgid "Closed" msgstr "Closed" msgid "Closing" msgstr "Closing" #, python-format msgid "Confirm %s" msgstr "Confirm %s" msgid "Confirm Delete Foobars" msgstr "Confirm Delete Foobars" msgid "Connecting" msgstr "Connecting" msgid "Could not decrypt the password" msgstr "Could not decrypt the password" msgid "Could not read the file" msgstr "Could not read the file" msgid "Create Subnet" msgstr "Create Subnet" msgid "Current Usage" msgstr "Current Usage" msgid "Custom" msgstr "Custom" msgid "Customization Script" msgstr "Customisation Script" msgid "Danger" msgstr "Danger" msgid "Danger: " msgstr "Danger: " msgid "Decimal required" msgstr "Decimal required" msgid "Delete" msgstr "Delete" msgid "Delete Instance" msgstr "Delete Instance" msgid "Delete Interface" msgstr "Delete Interface" msgid "Delete Network" msgstr "Delete Network" msgid "Delete Router" msgstr "Delete Router" msgid "Delete Subnet" msgstr "Delete Subnet" #, python-format msgid "Deleted : %s." msgstr "Deleted : %s." msgid "Detail Information" msgstr "Detail Information" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "Displaying %(count)s of %(total)s items" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "Displaying %s item" msgstr[1] "Displaying %s items" msgid "Duplicate keys are not allowed" msgstr "Duplicate keys are not allowed" msgid "Error" msgstr "Error" msgid "Error: " msgstr "Error: " msgid "Example" msgstr "Example" msgid "Existing Metadata" msgstr "Existing Metadata" msgid "Expand to see allocated items" msgstr "Expand to see allocated items" msgid "Expand to see available items" msgstr "Expand to see available items" msgid "Filter" msgstr "Filter" msgid "Finish" msgstr "Finish" msgid "Flavor" msgstr "Flavour" msgid "Full Text Search" msgstr "Full Text Search" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "IP Addresses" msgid "Info" msgstr "Info" msgid "Integer required" msgstr "Integer required" msgid "Interfaces" msgstr "Interfaces" msgid "Load script from a file" msgstr "Load script from a file" msgid "Loading" msgstr "Loading" msgid "Max" msgstr "Max" msgid "Max length" msgstr "Max length" msgid "Min" msgstr "Min" msgid "Min length" msgstr "Min length" msgid "Name" msgstr "Name" msgid "Next" msgstr "Next" msgid "No" msgstr "No" msgid "No Limit" msgstr "No Limit" msgid "No available items" msgstr "No available items" msgid "No available metadata" msgstr "No available metadata" msgid "No data available." msgstr "No data available." msgid "No description available." msgstr "No description available." msgid "No existing metadata" msgstr "No existing metadata" msgid "No items to display." msgstr "No items to display." msgid "No roles" msgstr "No roles" msgid "None" msgstr "None" msgid "Not authorized to do this operation." msgstr "Not authorised to do this operation." msgid "Notice: " msgstr "Notice: " msgid "Open" msgstr "Open" msgid "Open Console" msgstr "Open Console" msgid "Passwords do not match." msgstr "Passwords do not match." msgid "Pattern mismatch" msgstr "Pattern mismatch" msgid "Please confirm your selection. " msgstr "Please confirm your selection. " msgid "Prompt" msgstr "Prompt" msgid "Re-order items using drag and drop" msgstr "Re-order items using drag and drop" msgid "Remaining" msgstr "Remaining" msgid "Remove" msgstr "Remove" msgid "Required" msgstr "Required" msgid "Roles" msgstr "Roles" msgid "STATUS" msgstr "STATUS" #. Strings between {$ and $} should be left untranslated. msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgstr "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgid "Search in current results" msgstr "Search in current results" msgid "Select an item from Available items below" msgstr "Select an item from Available items below" msgid "Select one" msgstr "Select one" msgid "Server Name" msgstr "Server Name" msgid "Shutdown" msgstr "Shutdown" msgid "Status" msgstr "Status" #, python-format msgid "Status: %s" msgstr "Status: %s" msgid "Submit" msgstr "Submit" msgid "Subnets" msgstr "Subnets" msgid "Success" msgstr "Success" msgid "Success: " msgstr "Success: " msgid "Text" msgstr "Text" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgid "The script is larger than the maximum size" msgstr "The script is larger than the maximum size" msgid "There was a problem communicating with the server, please try again." msgstr "There was a problem communicating with the server, please try again." msgid "There was an error submitting the form. Please try again." msgstr "There was an error submitting the form. Please try again." msgid "Toggle Dropdown" msgstr "Toggle Dropdown" msgid "Toggle navigation" msgstr "Toggle navigation" msgid "Total" msgstr "Total" #, python-format msgid "Unable to delete: %s." msgstr "Unable to delete: %s." msgid "Unlimited" msgstr "Unlimited" msgid "View Details" msgstr "View Details" msgid "View Instance Details" msgstr "View Instance Details" msgid "View Router Details" msgstr "View Router Details" msgid "Warning" msgstr "Warning" msgid "Warning: " msgstr "Warning: " msgid "Working" msgstr "Working" msgid "Yes" msgstr "Yes" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." #, python-format msgid "You have selected %s. " msgstr "You have selected %s. " msgid "description" msgstr "description" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "pool default" #, python-format msgid "selected \"%s\"" msgstr "selected \"%s\"" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || 'No items to display.' $}" horizon-9.0.0/horizon/locale/pa_IN/0000775000567000056710000000000012701407231020263 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/pa_IN/LC_MESSAGES/0000775000567000056710000000000012701407231022050 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/pa_IN/LC_MESSAGES/django.po0000664000567000056710000002214312701407063023657 0ustar jenkinsjenkins00000000000000# Amandeep Singh Saini , 2015. #zanata # OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2015-10-06 02:02+0000\n" "Last-Translator: Amandeep Singh Saini \n" "Language-Team: Punjabi (India)\n" "Language: pa-IN\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " ਜੇ ਤà©à¨¹à¨¾à¨¨à©‚à©° ਇਹ ਪੱਕਾ ਨਹੀਂ ਕਿ ਕਿਹੜਾ ਪà©à¨°à¨®à¨¾à¨£à¨¿à¨•ਤਾ ਤਰੀਕਾ ਵਰਤਣਾ ਹੈ ਤਾਂ, ਆਪਣੇ ਪà©à¨°à¨¸à¨¼à¨¾à¨¸à¨¼à¨• ਨਾਲ " "ਰਾਬਤਾ ਕਰੋ।\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d ਬਾਈਟ" msgstr[1] "%(size)d ਬਾਈਟਾਂ" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s ਸਫਲਤਾ ਪੂਰਵਕ ਪੂਰਾ ਹੋਇਆ।" #, python-format msgid "%s did not complete." msgstr "%s ਪੂਰਾ ਨਹੀੰ ਹੋਇਆ।" msgid "« Prev" msgstr "« Prev" msgid "(No Limit)" msgstr "(ਕੋਈ ਹੱਦ ਨਹੀਂ)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 ਬਾਈਟਾਂ" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "ਇੱਕ %(resource)s ਪਹਿਲਾਂ ਹੀ \"%(name)s\" ਨਾਂ ਨਾਲ ਮੌਜੂਦ ਹੈ।" msgid "Actions" msgstr "ਕਾਰਵਾਈਆਂ" msgid "Active Instances:" msgstr "ਕਿਰਿਆਸ਼ੀਲ ਇੰਸਟਾਸਜ਼:" msgid "Active RAM:" msgstr "ਕਿਰਿਆਸ਼ੀਲ RAM:" msgid "Add a row" msgstr "ਇੱਕ ਸਤਰà©à¨¹ ਜੋੜੋ" msgid "All available" msgstr "ਸਾਰੇ ਉਪਲੱਬਧ" msgid "Available" msgstr "ਉਪਲੱਬਧ" msgid "Back" msgstr "ਪਿੱਛੇ" msgid "Cancel" msgstr "ਰੱਦ ਕਰੋ" msgid "Connect" msgstr "ਜà©à©œà©‹" msgid "Delete" msgstr "ਮਿਟਾਉ" msgid "Deleted" msgstr "ਮਿਟਾਇਆ ਗਿਆ" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "%(content_items)s ਚੀਜ਼ ਵਿਖਾ ਰਿਹਾ" msgstr[1] "%(content_items)s ਚੀਜ਼ਾਂ ਵਿਖਾ ਰਿਹਾ" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "%(counter)s ਚੀਜ ਵਿਖਾਈ ਜਾ ਰਹੀ" msgstr[1] "%(counter)s ਚੀਜਾਂ ਵਿਖਾਈਆਂ ਜਾ ਰਹੀਆਂ" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "%(nav_items)s ਚੀਜ਼ ਵਿਖਾ ਰਿਹਾ" msgstr[1] "%(nav_items)s ਚੀਜ਼ਾਂ ਵਿਖਾ ਰਿਹਾ" msgid "Error: " msgstr "ਗਲਤੀ: " msgid "Fake" msgstr "ਨਕਲੀ" msgid "Filter" msgstr "ਛਾਣਨੀ (ਫਿਲਟਰ)" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "IP ਪਤੇ ਲਈ ਗਲਤ ਬਣਤਰ" msgid "Info: " msgstr "ਜਾਣਕਾਰੀ: " msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "ਗਲਤ ਮੈਟਾਡੇਟਾ ਇੰਦਰਾਜ। ਕੌਮਿਆਂ ਨਾਲ ਵੱਖ ਕੀਤੇ ਹੋਠkey=value ਜੋੜੇ ਹੀ ਵਰਤੋ" msgid "Invalid subnet mask" msgstr "ਅਢà©à¨•ਵਾਂ ਸਬਨੈੱਟ ਮਾਸਕ" msgid "Invalid version for IP address" msgstr "IP ਪਤੇ ਲਈ ਅਢà©à¨•ਵਾਂ ਸੰਸਕਰਣ" msgid "Limit Summary" msgstr "ਹੱਦ ਸਾਰ" msgid "Login" msgstr "ਲਾਗਇਨ" msgid "Members" msgstr "ਜੀਅ" msgid "More Actions" msgstr "ਹੋਰ ਕਾਰਵਾਈਆਂ" msgid "Navigation Item" msgstr "ਨੈਵੀਗੇਸ਼ਨ ਚੀਜ਼" msgid "Never" msgstr "ਕਦੇ ਨਹੀਂ" msgid "Next" msgstr "ਅਗਲਾ" msgid "Next »" msgstr "Next »" msgid "No items to display." msgstr "ਪਰਦਰਸ਼ਿਤ ਕਰਨ ਲਈ ਕੋਈ ਚੀਜ਼ ਨਹੀਂ।" #, python-format msgid "No match returned for the id \"%s\"." msgstr "id \"%s\" ਲਈ ਕà©à©±à¨ ਵੀ ਮੇਲ ਖਾਂਦਾ ਵਾਪਿਸ ਨਹੀਂ ਹੋਇਆ।" msgid "No members." msgstr "ਕੋਈ ਜੀਅ ਨਹੀਂ।" msgid "None available." msgstr "ਕੋਈ ਉਪਲੱਬਧ ਨਹੀਂ" msgid "Not a valid IP protocol number" msgstr "ਇੱਕ ਢà©à¨•ਵਾਂ IP ਜਾਬਤਾ ਅੰਕ ਨਹੀਂ" msgid "Not a valid port number" msgstr "ਇੱਕ ਢà©à¨•ਵਾਂ ਪੋਰਟ ਨੰਬਰ ਨਹੀਂ" msgid "One colon allowed in port range" msgstr "ਪੋਰਟ ਹੱਦ ਵਿੱਚ ਇੱਕ ਕੌਲਨ ਦੀ ਪਰਵਾਨਗੀ ਹੈ" msgid "Other" msgstr "ਹੋਰ" msgid "Password is not accepted" msgstr "ਪਛਾਣ-ਸ਼ਬਦ ਪਰਵਾਨਿਤ ਨਹੀਂ ਹੈ" msgid "Please log in to continue." msgstr "ਜਾਰੀ ਰਹਿਣ ਲਈ ਕਿਰਪਾ ਕਰ ਕੇ ਲਾਗਇਨ ਕਰੋ।" msgid "Please select a row before taking that action." msgstr "ਉਹ ਕਾਰਵਾਈ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਕਿਰਪਾ ਕਰ ਕੇ ਇੱਕ ਸਤਰà©à¨¹ ਚà©à¨£à©‹à¥¤" msgid "Processing..." msgstr "...ਕਾਰਵਾਈ ਚੱਲ ਰਹੀ ਹੈ" msgid "Save" msgstr "ਸੰਭਾਲੋ" #, python-format msgid "Select a %s to browse." msgstr "ਬਰਾਊਜ਼ ਕਰਨ ਲਈ ਇੱਕ %s ਚà©à¨£à©‹à¥¤" msgid "Select a period of time to query its usage:" msgstr "ਇਸਦੀ ਵਰਤੋਂ ਬਾਰੇ ਪà©à©±à¨›-ਗਿੱਛ ਲਈ ਇੱਕ ਸਮਾਂ ਅੰਤਰਾਲ ਚà©à¨£à©‹:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "ਪਪੀ ਵੇਚੋ" msgstr[1] "ਪਪੀ ਵੇਚੋ" msgid "Sign In" msgstr "ਸਾਈਨ ਇਨ ਕਰੋ" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "ਵਿਕਿਆ ਹੋਇਆ ਪਪੀ" msgstr[1] "ਵਿਕੇ ਹੋਠਪਪੀ" msgid "Submit" msgstr "ਦਾਖਲ ਕਰੋ" msgid "Success: " msgstr "ਸਫਲਤਾ: " msgid "Summary" msgstr "ਸਾਰ" msgid "The date should be in YYYY-mm-dd format." msgstr "ਮਿਤੀ YYYY-mm-dd ਤਰਜ ਤੇ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ।" msgid "The string may only contain ASCII printable characters." msgstr "ਇਸ ਸਤਰà©à¨¹ ਵਿੱਚ ਸਿਰਫ਼ ਛਾਪੇ ਜਾ ਸਕਣ ਵਾਲੇ ASCII ਅੱਖਰ ਹੀ ਸ਼ਾਮਲ ਹੋ ਸਕਦੇ ਹਨ।" msgid "This Period's GB-Hours:" msgstr "ਇਸ ਅੰਤਰਾਲ ਦੇ GB-ਘੰਟੇ:" msgid "This Period's RAM-Hours:" msgstr "ਇਸ ਅੰਤਰਾਲ ਦੇ RAM-ਘੰਟੇ:" msgid "This Period's VCPU-Hours:" msgstr "ਇਸ ਅੰਤਰਾਲ ਦੇ VCPU-ਘੰਟੇ:" msgid "This action cannot be undone." msgstr "ਇਹ ਕਾਰਵਾਈ ਵਾਪਸ ਨਹੀੰ ਲਈ ਜਾ ਸਕੇਗੀ।" #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "%(action)s ਤੋਂ ਅਸਮਰੱਥ: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "ਅਣਅਧਿਕਾਰਤ। ਕਿਰਪਾ ਕਰ ਕੇ ਦà©à¨¬à¨¾à¨°à¨¾ ਲਾਗਇਨ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" #, python-format msgid "Unauthorized: %s" msgstr "ਅਣ-ਅਧਿਕਾਰਤ ਕੀਤਾ: %s" msgid "Usage Summary" msgstr "ਵਰਤੋਂ ਸਾਰ" msgid "Warning: " msgstr "ਚੇਤਾਵਨੀ: " #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "ਤà©à¨¹à¨¾à¨¨à©‚à©° %(action)s: ਦੀ ਮਨਜੂਰੀ ਨਹੀਂ ਹੈ: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "ਤà©à¨¸à©€à¨‚ %s ਤੇ ਦਖਲ ਦੇਣ ਲਈ ਅਧਿਕਾਰਤ ਨਹੀਂ ਹੋ" msgid "You do not have permission to access the resource:" msgstr "ਤà©à¨¹à¨¾à¨¡à©‡ ਕੋਲ ਵਸੀਲੇ ਤੱਕ ਦਖਲ ਦੀ ਪਰਵਾਨਗੀ ਨਹੀਂ ਹੈ:" horizon-9.0.0/horizon/locale/pa_IN/LC_MESSAGES/djangojs.po0000664000567000056710000001651612701407063024223 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Punjabi (India)\n" "Language: pa-IN\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s ਬਾਈਟਾਂ" msgid "Active" msgstr "ÙØ¹Ø§Ù„ كریں" msgid "Add Interface" msgstr "ਇੰਟਰਫੇਸ ਜੋੜà©à¨¹à©‹" msgid "Allocated" msgstr "ਵੰਡਿਆ ਜਾ ਚà©à©±à¨•ਾ" msgid "An error occurred while updating." msgstr "ਅੱਪਡੇਟ ਕਰਨ ਵੇਲੇ ਇੱਕ ਗਲਤੀ ਹੋਈ।" msgid "An error occurred. Please try again later." msgstr "ਇੱਕ ਗਲਤੀ ਵਾਪਰੀ। ਕਿਰਪਾ ਕਰ ਕੇ ਬਾਅਦ ਵਿੱਚ ਮà©à©œ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" msgid "Available" msgstr "ਉਪਲੱਬਧ" msgid "Available Metadata" msgstr "ਉਪਲੱਬਧ ਮੈਟਾਡਾਟਾ" msgid "Back" msgstr "ਪਿੱਛੇ" msgid "Cancel" msgstr "ਰੱਦ ਕਰੋ" msgid "Click here to expand the row and view the errors." msgstr "ਸਤਰà©à¨¹ ਨੂੰ ਫੈਲਾਉਣ ਤੇ ਗਲਤੀਆਂ ਨੂੰ ਵੇਖਣ ਲਈ ਇੱਥੇ ਕਲਿੱਕ ਕਰੋ।" msgid "Click to see more details" msgstr "ਹੋਰ ਵੇਰਵੇ ਵੇਖਣ ਲਈ ਕਲਿੱਕ ਕਰੋ" msgid "Click to show or hide" msgstr "ਵਿਖਾਉਣ ਜਾਂ ਲà©à¨•ਾਉਣ ਲਈ ਕਲਿੱਕ ਕਰੋ" msgid "Closed" msgstr "ਬੰਦ ਹੋਇਆ" msgid "Closing" msgstr "ਬੰਦ ਕਰ ਰਿਹਾ" #, python-format msgid "Confirm %s" msgstr "%s ਦੀ ਪà©à¨¶à¨Ÿà©€ ਕਰੋ" msgid "Connecting" msgstr "ਜà©à©œ ਰਿਹਾ" msgid "Could not decrypt the password" msgstr "ਪਛਾਣ-ਸ਼ਬਦ ਨੂੰ ਡੀ-ਕà©à¨°à¨¿à¨ªà¨Ÿ ਨਹੀਂ ਕਰ ਸਕਿਆ" msgid "Could not read the file" msgstr "ਫਾਈਲ ਪੜà©à¨¹ ਨਹੀਂ ਸਕਿਆ" msgid "Custom" msgstr "ਚà©à¨£à¨¿à©°à¨¦à¨¾" msgid "Danger" msgstr "ਖਤਰਾ" msgid "Danger: " msgstr "ਖਤਰਾ:" msgid "Decimal required" msgstr "ਦਸ਼ਮਲਵ ਲੋੜੀਂਦਾ" msgid "Delete" msgstr "ਮਿਟਾਉ" msgid "Delete Interface" msgstr "ਇੰਟਰਫੇਸ ਮਿਟਾਉ" msgid "Delete Router" msgstr "ਰਊਟਰ ਮਿਟਾਉ" msgid "Detail Information" msgstr "ਵਿਸਥਾਰ ਜਾਣਕਾਰੀ" msgid "Duplicate keys are not allowed" msgstr "ਨਕਲ ਚਾਬੀਆਂ ਪਰਵਾਨਤ ਨਹੀਂ ਹਨ" msgid "Error" msgstr "ਗਲਤੀ" msgid "Error: " msgstr "ਗਲਤੀ: " msgid "Existing Metadata" msgstr "ਮੌਜੂਦਾ ਮੈਟਾਡਾਟਾ" msgid "Expand to see allocated items" msgstr "ਵੰਡ ਹੋ ਚà©à©±à¨•ੀਆਂ ਚੀਜ਼ਾਂ ਵੇਖਣ ਲਈ ਫੈਲਾਉ" msgid "Expand to see available items" msgstr "ਉਪਲੱਬਧ ਚੀਜ਼ਾਂ ਵੇਖਣ ਲਈ ਫੈਲਾਉ" msgid "Filter" msgstr "ਛਾਣਨੀ (ਫਿਲਟਰ)" msgid "Finish" msgstr "ਮà©à¨•ੰਮਲ" msgid "Flavor" msgstr "Ùلیور" msgid "ID" msgstr "ID" msgid "Info" msgstr "ਜਾਣਕਾਰੀ" msgid "Integer required" msgstr "ਅੰਕ ਲੋੜੀਂਦਾ" msgid "Interfaces" msgstr "ਇੰਟਰਫੇਸ" msgid "Loading" msgstr "ਲੋਡ ਹੋ ਰਿਹਾ" msgid "Max" msgstr "ਵੱਧੋ-ਵੱਧ" msgid "Max length" msgstr "ਵੱਧੋ-ਵੱਧ ਲੰਬਾਈ" msgid "Min" msgstr "ਘੱਟੋ-ਘੱਟ" msgid "Min length" msgstr "ਘੱਟੋ-ਘੱਟ ਲੰਬਾਈ" msgid "Name" msgstr "ਨਾਂ" msgid "Next" msgstr "ਅਗਲਾ" msgid "No" msgstr "ਨਹੀਂ" msgid "No available items" msgstr "ਕੋਈ ਉਪਲੱਬਧ ਚੀਜ਼ ਨਹੀਂ" msgid "No available metadata" msgstr "ਕੋਈ ਮੈਟਾਡਾਟਾ ਉਪਲੱਬਧ ਨਹੀਂ" msgid "No data available." msgstr "ਕੋਈ ਡਾਟਾ ਉਪਲੱਬਧ ਨਹੀ।" msgid "No existing metadata" msgstr "ਕੋਈ ਮੌਜੂਦਾ ਮੈਟਾਡਾਟਾ ਨਹੀਂ" msgid "No items to display." msgstr "ਪਰਦਰਸ਼ਿਤ ਕਰਨ ਲਈ ਕੋਈ ਚੀਜ਼ ਨਹੀਂ।" msgid "No roles" msgstr "ਕੋਈ ਰੋਲ ਨਹੀੰ" msgid "None" msgstr "ਕੋਈ ਨਹੀਂ" msgid "Not authorized to do this operation." msgstr "ਇਹ ਕਾਰਵਾਈ ਕਰਨ ਲਈ ਅਧਿਕਾਰਤ ਨਹੀੰ ਕੀਤਾ ਹੋਇਆ।" msgid "Notice: " msgstr "ਸੂਚਨਾ:" msgid "Open" msgstr "ਖੋਲà©à¨¹à©‹" msgid "Open Console" msgstr "ਕੰਸੋਲ ਖੋਲà©à¨¹à©‹" msgid "Passwords do not match." msgstr "ਪਛਾਣ-ਸ਼ਬਦ ਮੇਲ ਨਹੀੰ ਖਾਂਦੇ।" msgid "Pattern mismatch" msgstr "ਬੇਮੇਲ ਨਮੂਨਾ" msgid "Please confirm your selection. " msgstr "ਕਿਰਪਾ ਕਰ ਕੇ ਆਪਣੀ ਚੋਣ ਦੀ ਪà©à¨¶à¨Ÿà©€ ਕਰੋ।" msgid "Re-order items using drag and drop" msgstr "ਧੂਹ ਕੇ ਸà©à©±à¨Ÿà¨£ (ਡਰੈਗ à¨à©°à¨¡ ਡਰੌਪ) ਨਾਲ ਚੀਜ਼ਾਂ ਨੂੰ ਮà©à©œ ਤਰਤੀਬ ਦਿਉ" msgid "Remove" msgstr "نكالیں" msgid "Required" msgstr "ਲੋੜੀਂਦਾ" msgid "Roles" msgstr "ਰੋਲ" msgid "STATUS" msgstr "ਹਾਲਾਤ" msgid "Select an item from Available items below" msgstr "ਹੇਠਾਂ ਉਪਲੱਬਧ ਚੀਜ਼ਾਂ ਵਿੱਚੋਂ ਇੱਕ ਚੀਜ਼ ਚà©à¨£à©‹" msgid "Select one" msgstr "ਇੱਕ ਚà©à¨£à©‹" msgid "Status" msgstr "ਹਾਲਾਤ" #, python-format msgid "Status: %s" msgstr "ਹਾਲਾਤ: %s" msgid "Submit" msgstr "ਦਾਖਲ ਕਰੋ" msgid "Success" msgstr "ਸਫਲਤਾ" msgid "Success: " msgstr "ਸਫਲਤਾ: " msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "ਇਹ ਕਾਰਵਾਈ ਨਹੀਂ ਹੋ ਸਕਦੀ। ਇਸ ਸਤਰà©à¨¹ ਦੇ ਅੰਸ਼ਾਂ ਵਿੱਚ ਗਲਤੀਆਂ ਹਨ ਜਾਂ ਕੋਈ ਜਾਣਕਾਰੀ ਘੱਟ ਹੈ।" msgid "There was a problem communicating with the server, please try again." msgstr "ਸਰਵਰ ਨਾਲ ਸੰਚਾਰ ਕਰਨ ਵੇਲੇ ਇੱਕ ਮà©à¨¶à¨•ਿਲ ਸੀ, ਕਿਰਪਾ ਕਰ ਕੇ ਮà©à©œ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" msgid "There was an error submitting the form. Please try again." msgstr "ਫਾਰਮ ਸਮਰਪਣ ਵੇਲੇ ਇੱਕ ਗਲਤੀ ਸੀ। ਕਿਰਪਾ ਕਰ ਕੇ ਫਿਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" msgid "View Details" msgstr "ਵੇਰਵੇ ਵੇਖੋ" msgid "View Instance Details" msgstr "ਇੰਸਟਾਸ ਦੇ ਵੇਰਵੇ ਵੇਖੋ" msgid "View Router Details" msgstr "ਰਊਟਰ ਦੇ ਵੇਰਵੇ ਵੇਖੋ" msgid "Warning" msgstr "ਚੇਤਾਵਨੀ" msgid "Warning: " msgstr "ਚੇਤਾਵਨੀ: " msgid "Working" msgstr "ਕੰਮ ਕਰ ਰਿਹਾ" msgid "Yes" msgstr "ਹਾਂ" #, python-format msgid "You have selected %s. " msgstr "ਤà©à¨¸à©€à¨‚ %s ਦੀ ਚੋਣ ਕੀਤੀ ਹੈ।" horizon-9.0.0/horizon/locale/cs/0000775000567000056710000000000012701407231017702 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/cs/LC_MESSAGES/0000775000567000056710000000000012701407231021467 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/cs/LC_MESSAGES/django.po0000664000567000056710000002041312701407063023274 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata # ZbynÄ›k Schwarz , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2015-12-21 11:00+0000\n" "Last-Translator: ZbynÄ›k Schwarz \n" "Language-Team: Czech\n" "Language: cs\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " PÅ™ihlaste se jako jiný uživatel nebo se vraÅ¥te na domovskou stránku\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " Využito %(used)s (Bez limitu)\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " Využito %(used)s z %(available)s \n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " Pokud nevíte, kterou metodu ověření použít, kontaktujte svého správce " "systému.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d bajt" msgstr[1] "%(size)d bajty" msgstr[2] "%(size)d bajtů" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s úspěšnÄ› dokonÄeno" #, python-format msgid "%s did not complete." msgstr "%s nedokonÄeno" msgid "« Prev" msgstr "« ZpÄ›t" msgid "(No Limit)" msgstr "(Bez limitu)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 bajtů" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "%(resource)s s názvem \"%(name)s\" již existuje" msgid "Actions" msgstr "ÄŒinnost" msgid "Active Instances:" msgstr "Aktivní instance:" msgid "Active RAM:" msgstr "Aktivní RAM:" msgid "Add a row" msgstr "PÅ™idat řádek" msgid "All available" msgstr "VÅ¡e dostupné" msgid "Available" msgstr "Dostupné" msgid "Back" msgstr "ZpÄ›t" msgid "Cancel" msgstr "ZruÅ¡it" msgid "Connect" msgstr "PÅ™ipojit" msgid "Delete" msgstr "Smazat" msgid "Deleted" msgstr "Smazáno" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Zobrazena %(content_items)s položka" msgstr[1] "Zobrazeny %(content_items)s položky" msgstr[2] "Zobrazeno %(content_items)s položek" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Zobrazena %(counter)s položka" msgstr[1] "Zobrazeny %(counter)s položky" msgstr[2] "Zobrazeno %(counter)s položek" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Zobrazena %(nav_items)s položka" msgstr[1] "Zobrazeny %(nav_items)s položky" msgstr[2] "Zobrazeno %(nav_items)s položek" msgid "Error: " msgstr "Chyba:" msgid "Fake" msgstr "FaleÅ¡né" msgid "Filter" msgstr "Filtr" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Nesprávný formát IP adresy" msgid "Info: " msgstr "Informace:" msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "" "Neplatná položka popisných dat. Použijte Äárkou oddÄ›lené páry klíÄ=hodnota" msgid "Invalid subnet mask" msgstr "Neplatná maska podsítÄ›" msgid "Invalid version for IP address" msgstr "Neplatná verze IP adresy" msgid "Limit Summary" msgstr "Omezit souhrn" msgid "Log in" msgstr "PÅ™ihlášení" msgid "Login" msgstr "PÅ™ihlášení" msgid "Members" msgstr "ÄŒlenové" msgid "More Actions" msgstr "Další Äinnosti" msgid "Navigation Item" msgstr "Položka navigace" msgid "Never" msgstr "Nikdy" msgid "Next" msgstr "Další" msgid "Next »" msgstr "Další »" msgid "No items to display." msgstr "Žádné položky k zobrazení." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Nebyla nalezena shoda s id \"%s\"." msgid "No members." msgstr "Žádní Älenové." msgid "None available." msgstr "Žádné dostupné." msgid "Not a valid IP protocol number" msgstr "Není platné Äíslo protokolu IP" msgid "Not a valid port number" msgstr "Není platné Äíslo portu" msgid "One colon allowed in port range" msgstr "V rozsahu portů povolena pouze jedna pomlÄka" msgid "Other" msgstr "Ostatní" msgid "Password is not accepted" msgstr "Heslo není pÅ™ijato" msgid "Please log in to continue." msgstr "K pokraÄování je nutno se pÅ™ihlásit." msgid "Please select a row before taking that action." msgstr "PÅ™ed provedením této Äinnosti prosím zvolte řádky." msgid "Processing..." msgstr "Zpracovávaní..." msgid "Save" msgstr "Uložit" #, python-format msgid "Select a %s to browse." msgstr "Vyberte %s pro prohlížení." msgid "Select a period of time to query its usage:" msgstr "Vyberte Äasové období pro zobrazení využití:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Prodat Å¡tÄ›nÄ›" msgstr[1] "Prodat Å¡těňata" msgstr[2] "Prodat Å¡těňat" msgid "Sign In" msgstr "PÅ™ihlásit" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Å tÄ›nÄ› prodáno" msgstr[1] "Å těňata prodána" msgstr[2] "Å těňata prodána" msgid "Submit" msgstr "Odeslat" msgid "Success: " msgstr "ÚspÄ›ch:" msgid "Summary" msgstr "PÅ™ehled" msgid "The date should be in YYYY-mm-dd format." msgstr "Datum by mÄ›lo být ve formátu RRRR-mm-dd." msgid "The string may only contain ASCII printable characters." msgstr "ŘetÄ›zec může obsahovat pouze zobrazitelné znaky ASCII." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "Hodnota %(resource)s je %(name)s uvnitÅ™ Å¡ablony. PÅ™i spuÅ¡tÄ›ní zásobníku z " "tohoto rozhraní musí hodnota zaÄínat \"http://\" nebo \"https://\"" msgid "This Period's GB-Hours:" msgstr "GB hodin za toto období:" msgid "This Period's RAM-Hours:" msgstr "RAM hodin za toto období:" msgid "This Period's VCPU-Hours:" msgstr "VCPU hodin za toto období:" msgid "This action cannot be undone." msgstr "Toto nelze vrátit." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Nelze %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "NeoprávnÄ›ný přístup. Prosím zkuste se pÅ™ihlásit znovu." #, python-format msgid "Unauthorized: %s" msgstr "NeoprávnÄ›né: %s" msgid "Usage Summary" msgstr "Souhrn využití" msgid "Warning: " msgstr "Varování:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Nemáte oprávnÄ›ní pro %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Nemá oprávnÄ›ní k přístupu %s" msgid "You do not have permission to access the resource:" msgstr "Nemáte oprávnÄ›ní k přístupu do zdroje:" horizon-9.0.0/horizon/locale/cs/LC_MESSAGES/djangojs.po0000664000567000056710000001676012701407063023643 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata # ZbynÄ›k Schwarz , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2015-12-08 03:42+0000\n" "Last-Translator: ZbynÄ›k Schwarz \n" "Language-Team: Czech\n" "Language: cs\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s bajtů" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "Aktivní" msgid "Add Interface" msgstr "PÅ™idat rozhraní" msgid "Added" msgstr "PÅ™idáno" msgid "Allocated" msgstr "PÅ™idÄ›leno" msgid "An error occurred while updating." msgstr "PÅ™i aktualizaci nastal problém." msgid "An error occurred. Please try again later." msgstr "Vyskytla se chyba. Prosím zkuste to znovu pozdÄ›ji." msgid "Available" msgstr "Dostupné" msgid "Available Metadata" msgstr "Dostupná popisná data" msgid "Back" msgstr "ZpÄ›t" msgid "Cancel" msgstr "ZruÅ¡it" msgid "Click here for filters." msgstr "KliknÄ›te zde pro zobrazení filtrů" msgid "Click here to expand the row and view the errors." msgstr "KliknÄ›te zde pro rozbalení řádku a zobrazení chyb." msgid "Click to see more details" msgstr "KliknÄ›te pro podrobnosti" msgid "Click to show or hide" msgstr "KliknÄ›te zde pro zobrazení nebo skrytí" msgid "Closed" msgstr "ZavÅ™eno" msgid "Closing" msgstr "Zavírání" #, python-format msgid "Confirm %s" msgstr "Potvrdit %s" msgid "Confirm Delete Foobars" msgstr "Potvrdit smazání Foobar" msgid "Connecting" msgstr "PÅ™ipojování" msgid "Could not decrypt the password" msgstr "Heslo nelze deÅ¡ifrovat" msgid "Could not read the file" msgstr "Soubor nelze pÅ™eÄíst" msgid "Create Subnet" msgstr "VytvoÅ™it podsíť" msgid "Current Usage" msgstr "SouÄasné využití" msgid "Custom" msgstr "Vlastní" msgid "Danger" msgstr "NebezpeÄí" msgid "Danger: " msgstr "NebezpeÄí:" msgid "Decimal required" msgstr "Vyžadováno desetinné Äíslo" msgid "Delete" msgstr "Smazat" msgid "Delete Instance" msgstr "Smazat instanci" msgid "Delete Interface" msgstr "Smazat rozhraní" msgid "Delete Network" msgstr "Smazat síť" msgid "Delete Router" msgstr "Smazat smÄ›rovaÄ" msgid "Delete Subnet" msgstr "Smazat podsíť" #, python-format msgid "Deleted : %s." msgstr "Smazáno : %s" msgid "Detail Information" msgstr "Podrobné informace" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "Zobrazování %(count)s z %(total)s položek" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "Zobrazena %s položka" msgstr[1] "Zobrazeny %s položky" msgstr[2] "Zobrazeno %s položek" msgid "Duplicate keys are not allowed" msgstr "Duplicitní klíÄe nejsou povoleny" msgid "Error" msgstr "Chyba" msgid "Error: " msgstr "Chyba:" msgid "Existing Metadata" msgstr "Existující popisná data" msgid "Expand to see allocated items" msgstr "Rozbalte pro zobrazení pÅ™idÄ›lených položek" msgid "Expand to see available items" msgstr "Rozbalte pro zobrazení dostupných položek" msgid "Filter" msgstr "Filtr" msgid "Finish" msgstr "DokonÄit" msgid "Flavor" msgstr "Konfigurace" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "IP adresy" msgid "Info" msgstr "Informace" msgid "Integer required" msgstr "Vyžadováno celé Äíslo" msgid "Interfaces" msgstr "Rozhraní" msgid "Loading" msgstr "NaÄítání" msgid "Max" msgstr "Maximum" msgid "Max length" msgstr "Maximální délka" msgid "Min" msgstr "Minimum" msgid "Min length" msgstr "Minimální délka" msgid "Name" msgstr "Jméno" msgid "Next" msgstr "Další" msgid "No" msgstr "Ne" msgid "No available items" msgstr "Žádné dostupné položky" msgid "No available metadata" msgstr "Žádná dostupná popisná data" msgid "No data available." msgstr "Data nejsou dostupná." msgid "No description available." msgstr "Není zadán žádný popis." msgid "No existing metadata" msgstr "Žádná existující popisná data" msgid "No items to display." msgstr "Žádné položky k zobrazení." msgid "No roles" msgstr "Žádné role" msgid "None" msgstr "Žádný" msgid "Not authorized to do this operation." msgstr "Nemáte oprávnÄ›ní provést tuto operaci." msgid "Notice: " msgstr "Oznámení:" msgid "Open" msgstr "Otevřít" msgid "Open Console" msgstr "Otevřít konzoli" msgid "Passwords do not match." msgstr "Hesla se neshodují." msgid "Pattern mismatch" msgstr "Neshoda vzoru" msgid "Please confirm your selection. " msgstr "PotvrÄte prosím váš výbÄ›r." msgid "Prompt" msgstr "Výzva" msgid "Re-order items using drag and drop" msgstr "ˇ5azení položek změňte pÅ™etažením" msgid "Remaining" msgstr "Zbývá" msgid "Remove" msgstr "Odstranit" msgid "Required" msgstr "Vyžadováno" msgid "Roles" msgstr "Role" msgid "STATUS" msgstr "STAV" msgid "Select an item from Available items below" msgstr "Vyberte položku ze seznamu níže" msgid "Select one" msgstr "Zvolte jeden" msgid "Server Name" msgstr "Název serveru" msgid "Shutdown" msgstr "Vypnout" msgid "Status" msgstr "Stav" #, python-format msgid "Status: %s" msgstr "Stav: %s" msgid "Submit" msgstr "Odeslat" msgid "Subnets" msgstr "PodsítÄ›" msgid "Success" msgstr "ÚspÄ›ch" msgid "Success: " msgstr "ÚspÄ›ch:" msgid "Text" msgstr "Text" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "ÄŒinnost nelze provést. Obsah tohoto řádku má chyby, nebo mu chybí informace." msgid "There was a problem communicating with the server, please try again." msgstr "PÅ™i komunikaci se serverem nastal problém, zkuste to znovu." msgid "There was an error submitting the form. Please try again." msgstr "PÅ™i odesílání formuláře nastal problém. Zkuste to prosím znovu." msgid "Toggle Dropdown" msgstr "PÅ™epnout rozbalovací seznam" msgid "Total" msgstr "Celkem" #, python-format msgid "Unable to delete: %s." msgstr "Nelze smazat: %s." msgid "View Details" msgstr "Zobrazit podrobnosti" msgid "View Instance Details" msgstr "Zobrazit podrobnosti instance" msgid "View Router Details" msgstr "Zobrazit podrobnosti smÄ›rovaÄe" msgid "Warning" msgstr "Varování" msgid "Warning: " msgstr "Varování:" msgid "Working" msgstr "Zpracovávání" msgid "Yes" msgstr "Ano" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "Popisná data zdroje můžete zadat pÅ™esunutím položek z levého sloupce do " "pravého. V levém sloupci jsou vypsány definice dat z katalogu popisných dat " "Glance. Použijte volbu \"Vlastní\" pro pÅ™idání klíÄe vaÅ¡eho výbÄ›ru." #, python-format msgid "You have selected %s. " msgstr "Vybrali jste %s." msgid "description" msgstr "popis" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "výchozí zásoby" #, python-format msgid "selected \"%s\"" msgstr "\"%s\" vybráno" horizon-9.0.0/horizon/locale/tr_TR/0000775000567000056710000000000012701407231020327 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/tr_TR/LC_MESSAGES/0000775000567000056710000000000012701407231022114 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/tr_TR/LC_MESSAGES/django.po0000664000567000056710000002315712701407071023730 0ustar jenkinsjenkins00000000000000# Mücahit Büyükyılmaz , 2015. #zanata # Steve Kowalik , 2015. #zanata # Mücahit Büyükyılmaz , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-25 08:57+0000\n" "Last-Translator: Mücahit Büyükyılmaz \n" "Language-Team: Turkish (Turkey)\n" "Language: tr-TR\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n>1)\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " Farklı bir kullanıcı olarak giriÅŸ yapın veya Ana Sayfaya dönün\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " %(used)s (Sınırsız) Kullanımda\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " %(used)s / %(available)s kullanımda\n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" "%(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" "Hangi kimlik doÄŸrulama yöntemini kullanacağınızdan emin deÄŸilseniz , " "yöneticinize baÅŸvurunuz.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Byte" msgstr[1] "%(size)d Byte" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s baÅŸarıyla tamamlandı." #, python-format msgid "%s did not complete." msgstr "%s tamamlanamadı." msgid "« Prev" msgstr "« Geri" msgid "(No Limit)" msgstr "(Limitsiz)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 Byte" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "\"%(name)s\" isimli bir %(resource)s zaten var." msgid "Actions" msgstr "İşlemler" msgid "Active Instances:" msgstr "Aktif Instancelar:" msgid "Active RAM:" msgstr "Aktif RAM:" msgid "Add a row" msgstr "Yeni bir satır ekle" msgid "All available" msgstr "Hepsi uygun" msgid "Available" msgstr "Uygun" msgid "Back" msgstr "Geri" #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "Toplu Öğe" msgstr[1] "Toplu Öğeler" #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "İzlenen Öğe" msgstr[1] "İzlenen Öğeler" msgid "Cancel" msgstr "İptal" msgid "Connect" msgstr "Baglantı" msgid "Delete" msgstr "Sil" msgid "Deleted" msgstr "Silindi" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "%(content_items)s öge görüntüleniyor" msgstr[1] "%(content_items)s öge görüntüleniyor" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "%(counter)s öge görüntüleniyor" msgstr[1] "%(counter)s öge görüntüleniyor" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "%(nav_items)s öge görüntüleniyor" msgstr[1] "%(nav_items)s öge görüntüleniyor" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "Öğeyi Duraklat" msgstr[1] "" #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "Duraklatılan Öğe" msgstr[1] "" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "Json mesaj dosyası iÅŸlenirken hata oluÅŸtu. '%(path)s': %(exception)s " msgid "Error: " msgstr "Hata:" msgid "Fake" msgstr "Sahte" msgid "Filter" msgstr "Filtre" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Hatalı IP adres formatı" msgid "Info: " msgstr "Bilgi:" msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "" "Geçersiz metadata girdisi. Virgülle ayrılmış anahtar=deÄŸer çiftleri kullanın" msgid "Invalid subnet mask" msgstr "Geçersiz subnet maskesi" msgid "Invalid version for IP address" msgstr "Geçersiz IP adres versiyonu" msgid "Limit Summary" msgstr "Özeti Sınırla" msgid "Log in" msgstr "GiriÅŸ" msgid "Login" msgstr "GiriÅŸ" msgid "Members" msgstr "Üyeler" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "Json mesaj dosyası '%(path)s' hatalı. %(exception)s" msgid "More Actions" msgstr "DiÄŸer İşlemler" msgid "Navigation Item" msgstr "Gezinme öğesi" msgid "Never" msgstr "Asla" msgid "Next" msgstr "İleri" msgid "Next »" msgstr "İleri »" msgid "No items to display." msgstr "Görüntülenecek öğe yok." #, python-format msgid "No match returned for the id \"%s\"." msgstr "\"%s\" ID'si için eÅŸleÅŸme bulunamadı." msgid "No members." msgstr "Üye yok." msgid "None available." msgstr "Hiçbiri uygun deÄŸil." msgid "Not a valid IP protocol number" msgstr "Geçersiz IP Protokol numarası" msgid "Not a valid port number" msgstr "Geçersiz port numarası" msgid "One colon allowed in port range" msgstr "Port aralığında tek bir kolon girilebilir" msgid "Other" msgstr "DiÄŸer" msgid "Password is not accepted" msgstr "Parola kabul edilmedi" msgid "Please log in to continue." msgstr "Devam etmek için lütfen oturum açınız." msgid "Please select a row before taking that action." msgstr "Bir iÅŸlem yapmadan önce lütfen bir satır seçin." msgid "Processing..." msgstr "İşleniyor..." msgid "Save" msgstr "Kaydet" #, python-format msgid "Select a %s to browse." msgstr "Göz atmak için bir %s seçin." msgid "Select a period of time to query its usage:" msgstr "Kullanımı listelemek için zaman aralığı seçin." #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Köpek Yavrusu Sat" msgstr[1] "Köpek Yavrularını Sat" msgid "Sign In" msgstr "Oturum Aç" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Satılan Yavru" msgstr[1] "Satılan Yavrular" msgid "Submit" msgstr "Gönder" msgid "Success: " msgstr "BaÅŸarılı:" msgid "Summary" msgstr "Özet" #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "%(attr)s özelliÄŸi %(obj)s üzerinde bulunamadı ." msgid "The date should be in YYYY-mm-dd format." msgstr "Tarih YYYY-AA-GG formatında olmalı." msgid "The string may only contain ASCII printable characters." msgstr "Metin sadece yazdırılabilir ASCII karakterleri içerebilir." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "Bu arayüz üzerinde yeni bir küme baÅŸlatırken, template içerisindeki %(name)s " "isimli %(resource)s \"http://\" veya \"https://\" ile baÅŸlamalıdır." msgid "This Period's GB-Hours:" msgstr "Bu periyodda kullanılan GB-Saat" msgid "This Period's RAM-Hours:" msgstr "Bu periyodda kullanılan RAM-Saat" msgid "This Period's VCPU-Hours:" msgstr "Bu periyodda kullanılan VCPU-Saat" msgid "This action cannot be undone." msgstr "Bu iÅŸlem geri alınamaz." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "%(action)s: %(objs)s iÅŸlemi yapılamıyor." msgid "Unauthorized. Please try logging in again." msgstr "Yetkisiz GiriÅŸ. Lütfen tekrar oturum açınız. " #, python-format msgid "Unauthorized: %s" msgstr "Yetkisiz: %s" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "Öğeyi Sürdür" msgstr[1] "" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "Öğeyi Güncelle" msgstr[1] "" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "Güncellenen Öğe" msgstr[1] "" #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "Sürdürülen Öğe" msgstr[1] "" msgid "Usage Summary" msgstr "Kullanım Özeti" msgid "Warning: " msgstr "Uyarı:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Bu iÅŸlem için izniniz yok %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "%s e eriÅŸim yetkiniz bulunmuyor" msgid "You do not have permission to access the resource:" msgstr "KaynaÄŸa eriÅŸim için yetkiniz bulunmuyor:" horizon-9.0.0/horizon/locale/tr_TR/LC_MESSAGES/djangojs.po0000664000567000056710000002043012701407071024254 0ustar jenkinsjenkins00000000000000# Mücahit Büyükyılmaz , 2015. #zanata # Steve Kowalik , 2015. #zanata # Mücahit Büyükyılmaz , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-25 09:00+0000\n" "Last-Translator: Mücahit Büyükyılmaz \n" "Language-Team: Turkish (Turkey)\n" "Language: tr-TR\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n>1)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s byte" msgid "(Modified)" msgstr "(Düzenlendi)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "Etkin" msgid "Add Interface" msgstr "Arayüz ekle" msgid "Added" msgstr "Eklendi" msgid "Allocated" msgstr "Ayrılmış" msgid "An error occurred while updating." msgstr "Güncelleme sırasında bir hata oluÅŸtu." msgid "An error occurred. Please try again later." msgstr "Bir hata oluÅŸtu. Lütfen daha sonra tekrar deneyiniz." msgid "Available" msgstr "Uygun" msgid "Available Metadata" msgstr "Geçerli üstveri" msgid "Back" msgstr "Geri" msgid "Cancel" msgstr "İptal" msgid "Click here for filters." msgstr "Filtre için buraya tıklayın" msgid "Click here to expand the row and view the errors." msgstr "Satırı geniÅŸletmek ve hataları görüntülemek için burayı tıklayın." msgid "Click to see more details" msgstr "Daha fazla detay için tıklayın" msgid "Click to show or hide" msgstr "Göstermek ve gizlemek için tıklayın" msgid "Closed" msgstr "Kapandı" msgid "Closing" msgstr "Kapatılıyor" #, python-format msgid "Confirm %s" msgstr "%s Onayla" msgid "Confirm Delete Foobars" msgstr "Foobar Silmeyi Onayla" msgid "Connecting" msgstr "BaÄŸlanıyor" msgid "Could not decrypt the password" msgstr "Parola Çözülemedi" msgid "Could not read the file" msgstr "Dosya okunamadı" msgid "Create Subnet" msgstr "Subnet OluÅŸtur" msgid "Current Usage" msgstr "Mevcut Kullanım" msgid "Custom" msgstr "Özel" msgid "Customization Script" msgstr "ÖzelleÅŸtirme Scripti" msgid "Danger" msgstr "Tehlike" msgid "Danger: " msgstr "Tehlike:" msgid "Decimal required" msgstr "Ondalıklı sayı gerekli" msgid "Delete" msgstr "Sil" msgid "Delete Instance" msgstr "Instance'ı sil" msgid "Delete Interface" msgstr "Arayüzü Sil" msgid "Delete Network" msgstr "AÄŸ Sil" msgid "Delete Router" msgstr "Router'ı Sil" msgid "Delete Subnet" msgstr "Subnet Sil" #, python-format msgid "Deleted : %s." msgstr "Silindi : %s." msgid "Detail Information" msgstr "Detaylı Bilgi" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "Toplam %(total)s öğenin %(count)s tanesi gösteriliyor" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "%s adet gösteriliyor" msgstr[1] "%s adet gösteriliyor" msgid "Duplicate keys are not allowed" msgstr "Tekrarlanan anahtarlara izin verilmiyor" msgid "Error" msgstr "Hata" msgid "Error: " msgstr "Hata:" msgid "Example" msgstr "Örnek" msgid "Existing Metadata" msgstr "Mevcut üstveri" msgid "Expand to see allocated items" msgstr "Ayrılmış öğeleri görmek için geniÅŸlet" msgid "Expand to see available items" msgstr "Uygun öğeleri görmek için geniÅŸlet" msgid "Filter" msgstr "Filtre" msgid "Finish" msgstr "Bitti" msgid "Flavor" msgstr "Åžablon" msgid "Full Text Search" msgstr "Tam Metin Araması" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "IP Adresleri" msgid "Info" msgstr "Bilgi" msgid "Integer required" msgstr "Tamsayı gerekli" msgid "Interfaces" msgstr "Arayüzler" msgid "Load script from a file" msgstr "Scripti dosyadan yükle" msgid "Loading" msgstr "Yükleniyor" msgid "Max" msgstr "Maksimum" msgid "Max length" msgstr "Maksimum Uzunluk" msgid "Min" msgstr "Minimum" msgid "Min length" msgstr "Minimum Uzunluk" msgid "Name" msgstr "Ad" msgid "Next" msgstr "İleri" msgid "No" msgstr "Hayır" msgid "No Limit" msgstr "Sınırsız" msgid "No available items" msgstr "Uygun öğe yok" msgid "No available metadata" msgstr "Uygun üstveri mevcut deÄŸil" msgid "No data available." msgstr "Veri yok." msgid "No description available." msgstr "Açıklama yok." msgid "No existing metadata" msgstr "Üstveri mevcut deÄŸil" msgid "No items to display." msgstr "Görüntülenecek öğe yok." msgid "No roles" msgstr "Rol yok" msgid "None" msgstr "Yok" msgid "Not authorized to do this operation." msgstr "Bu iÅŸlemi yapmak için yetkiniz yok." msgid "Notice: " msgstr "Dikkat:" msgid "Open" msgstr "Aç" msgid "Open Console" msgstr "Konsol Aç" msgid "Passwords do not match." msgstr "Parolalar uyuÅŸmuyor." msgid "Pattern mismatch" msgstr "Model UyumsuzluÄŸu" msgid "Please confirm your selection. " msgstr "Lütfen seçiminizi onaylayın" msgid "Prompt" msgstr "İleti" msgid "Re-order items using drag and drop" msgstr "Sürükle ve bırak kullanarak ögeleri yeniden sırala" msgid "Remaining" msgstr "Kalan" msgid "Remove" msgstr "Sil" msgid "Required" msgstr "Zorunlu" msgid "Roles" msgstr "Roller" msgid "STATUS" msgstr "DURUM" #. Strings between {$ and $} should be left untranslated. msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgstr "" "Script boyutu: {$ (scriptLength || 0) | bytes $} / {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgid "Search in current results" msgstr "Åžu andaki sonuçlar içinde Ara" msgid "Select an item from Available items below" msgstr "AÅŸağıdaki kullanılabilir öğelerden birini seçin" msgid "Select one" msgstr "Birini seçin" msgid "Server Name" msgstr "Sunucu Adı" msgid "Shutdown" msgstr "Kapat" msgid "Status" msgstr "Durum" #, python-format msgid "Status: %s" msgstr "Durum: %s" msgid "Submit" msgstr "Gönder" msgid "Subnets" msgstr "Subnetler" msgid "Success" msgstr "BaÅŸarılı" msgid "Success: " msgstr "BaÅŸarılı:" msgid "Text" msgstr "Metin" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "Bu iÅŸlem yapılamaz. Bu satırın içeriÄŸinde hatalar var veya eksik bilgi " "içeriyor." msgid "The script is larger than the maximum size" msgstr "Script maksimum boyuttan büyük" msgid "There was a problem communicating with the server, please try again." msgstr "Sunucu ile iletiÅŸimde bir sorun var, lütfen tekrar deneyiniz." msgid "There was an error submitting the form. Please try again." msgstr "Formu gönderirken bir hata oluÅŸtu. Lütfen tekrar deneyin." msgid "Toggle Dropdown" msgstr "Açılır Menüyü Tıklayın" msgid "Toggle navigation" msgstr "Gezintiyi deÄŸiÅŸtir" msgid "Total" msgstr "Toplam" #, python-format msgid "Unable to delete: %s." msgstr "%s silinemedi." msgid "Unlimited" msgstr "Sınırsız" msgid "View Details" msgstr "Ayrıntıları Göster" msgid "View Instance Details" msgstr "Instance Detaylarını Göster" msgid "View Router Details" msgstr "Router Ayrıntılarını Göster" msgid "Warning" msgstr "Uyarı" msgid "Warning: " msgstr "Uyarı:" msgid "Working" msgstr "Çalışıyor" msgid "Yes" msgstr "Evet" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "Kaynak üstveriyi sol sütundaki ögeleri saÄŸ sütuna taşıyarak " "belirtebilirsiniz.Sol sütunda Glance Üstveri kataloÄŸu'ndaki üstveri " "tanımları mevcuttur. Özel bir üstveri eklemek için \"DiÄŸer\" seçeneÄŸini " "kullanınız." #, python-format msgid "You have selected %s. " msgstr "%s yi seçtiniz." msgid "description" msgstr "açıklama" msgid "m1.small" msgstr "m1.küçük" msgid "m1.tiny" msgstr "m1.minik" msgid "pool default" msgstr "Ön tanımlı havuz" #, python-format msgid "selected \"%s\"" msgstr "seçildi \"%s\"" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || 'Gösterilecek öğe yok.' $}" horizon-9.0.0/horizon/locale/ca/0000775000567000056710000000000012701407231017660 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/ca/LC_MESSAGES/0000775000567000056710000000000012701407231021445 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/ca/LC_MESSAGES/django.po0000664000567000056710000000707412701407063023262 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Catalan\n" "Language: ca\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s completat correctament." #, python-format msgid "%s did not complete." msgstr "%s no s'ha completat" msgid "Actions" msgstr "Accions" msgid "All available" msgstr "Tots els disponibles" msgid "Available" msgstr "Disponible" msgid "Cancel" msgstr "Cancel·la" msgid "Delete" msgstr "Eliminar" msgid "Deleted" msgstr "Eliminat" msgid "Error: " msgstr "Error: " msgid "Filter" msgstr "Filtre" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Format incorrecte per l'adreça IP" msgid "Info: " msgstr "Informació:" msgid "Invalid subnet mask" msgstr "Màscara de subxarxa no vàlida" msgid "Invalid version for IP address" msgstr "Versió incorrecta de l'adreça IP" msgid "Limit Summary" msgstr "Resum Límit" msgid "Login" msgstr "Login" msgid "Members" msgstr "Membres" msgid "Navigation Item" msgstr "Article de navegació" msgid "No items to display." msgstr "No hi ha articles per mostrar." #, python-format msgid "No match returned for the id \"%s\"." msgstr "No hi ha resultats per l'identificador \"%s\"." msgid "No members." msgstr "No hi ha membres." msgid "None available." msgstr "Cap disponible." msgid "Not a valid IP protocol number" msgstr "No és un nombre de protocol IP vàlida" msgid "Not a valid port number" msgstr "No és un número de port vàlid" msgid "One colon allowed in port range" msgstr "Esta permès dos punts en rang de ports" msgid "Other" msgstr "Altre" msgid "Password is not accepted" msgstr "La contrasenya no ha estat acceptada" msgid "Please log in to continue." msgstr "Identifiqueu-vos per continuar." msgid "Please select a row before taking that action." msgstr "Heu de seleccionar una fila abans de dur a terme aquesta acció." msgid "Processing..." msgstr "Processant..." msgid "Save" msgstr "Desa" #, python-format msgid "Select a %s to browse." msgstr "Seleccionar un %s per navegar." msgid "Sign In" msgstr "Entrar" msgid "Submit" msgstr "Transmet" msgid "Success: " msgstr "Èxit:" msgid "Summary" msgstr "Resum" msgid "The date should be in YYYY-mm-dd format." msgstr "La data ha d'estar en YYYY-mm-dd format." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "No es pot %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Sense autorització. Torneu a intentar-ho." #, python-format msgid "Unauthorized: %s" msgstr "No autoritzat: %s" msgid "Usage Summary" msgstr "Resum de l'ús" msgid "Warning: " msgstr "Advertiment:" #, python-format msgid "You are not authorized to access %s" msgstr "No esteu autoritzats per accedir a %s" horizon-9.0.0/horizon/locale/djangojs.pot0000664000567000056710000004201312701407063021623 0ustar jenkinsjenkins00000000000000# Translations template for PROJECT. # Copyright (C) 2016 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # FIRST AUTHOR , 2016. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-08 06:14+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.2.0\n" #: horizon/static/framework/conf/resource-type-registry.service.spec.js:86 msgid "Example" msgstr "" #: horizon/static/framework/util/filters/filters.js:44 msgid "Yes" msgstr "" #: horizon/static/framework/util/filters/filters.js:44 msgid "No" msgstr "" #: horizon/static/framework/util/filters/filters.js:62 #: horizon/static/framework/util/filters/filters.js:187 #, python-format msgid "%s TB" msgstr "" #: horizon/static/framework/util/filters/filters.js:64 msgid "0 GB" msgstr "" #: horizon/static/framework/util/filters/filters.js:66 #: horizon/static/framework/util/filters/filters.js:85 #: horizon/static/framework/util/filters/filters.js:189 #, python-format msgid "%s GB" msgstr "" #: horizon/static/framework/util/filters/filters.js:87 msgid "0 MB" msgstr "" #: horizon/static/framework/util/filters/filters.js:89 #: horizon/static/framework/util/filters/filters.js:191 #, python-format msgid "%s MB" msgstr "" #: horizon/static/framework/util/filters/filters.js:137 msgid "-" msgstr "" #: horizon/static/framework/util/filters/filters.js:152 #: horizon/static/horizon/js/horizon.networktopology.js:928 #: horizon/static/horizon/js/horizon.networktopology.js:934 #: horizon/static/horizon/js/horizon.networktopology.js:940 msgid "None" msgstr "" #: horizon/static/framework/util/filters/filters.js:193 #, python-format msgid "%s KB" msgstr "" #: horizon/static/framework/util/filters/filters.js:195 #, python-format msgid "%s bytes" msgstr "" #: horizon/static/framework/util/filters/filters.js:220 #: horizon/static/horizon/js/horizon.tables.js:432 #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "" msgstr[1] "" #: horizon/static/framework/util/filters/filters.js:224 #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "" #: horizon/static/framework/util/filters/filters.js:255 msgid "Unlimited" msgstr "" #: horizon/static/framework/widgets/action-list/action-list.module.js:49 msgid "" "The action cannot be performed. The contents of this row have errors or " "are missing information." msgstr "" #: horizon/static/framework/widgets/action-list/button-tooltip.row-warning.service.js:41 msgid "Click here to expand the row and view the errors." msgstr "" #: horizon/static/framework/widgets/action-list/split-button.html:13 msgid "Toggle Dropdown" msgstr "" #: horizon/static/framework/widgets/charts/charts.module.js:107 msgid "Current Usage" msgstr "" #: horizon/static/framework/widgets/charts/charts.module.js:109 msgid "Added" msgstr "" #: horizon/static/framework/widgets/charts/charts.module.js:111 msgid "Remaining" msgstr "" #: horizon/static/framework/widgets/charts/pie-chart.directive.js:174 msgid "No Limit" msgstr "" #: horizon/static/framework/widgets/charts/pie-chart.directive.js:177 #: horizon/static/framework/widgets/metadata/tree/tree.module.js:48 msgid "Max" msgstr "" #: horizon/static/framework/widgets/charts/pie-chart.directive.js:182 msgid "Total" msgstr "" #: horizon/static/framework/widgets/load-edit/load-edit.html:3 msgid "Customization Script" msgstr "" #: horizon/static/framework/widgets/load-edit/load-edit.html:4 msgid "(Modified)" msgstr "" #. Strings between {$ and $} should be left untranslated. #: horizon/static/framework/widgets/load-edit/load-edit.html:9 msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ " "config.MAX_SCRIPT_SIZE | bytes $}" msgstr "" #: horizon/static/framework/widgets/load-edit/load-edit.html:20 msgid "The script is larger than the maximum size" msgstr "" #: horizon/static/framework/widgets/load-edit/load-edit.html:28 msgid "Load script from a file" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.directive.js:108 #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:35 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:54 #: horizon/static/framework/widgets/modal/simple-modal.service.js:82 #: horizon/static/framework/widgets/wizard/wizard.module.js:47 #: horizon/static/horizon/js/horizon.modals.js:33 msgid "Cancel" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.directive.js:109 msgid "Click here for filters." msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.directive.js:110 #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:37 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:56 msgid "Remove" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.directive.js:112 msgid "Search in current results" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.directive.js:113 msgid "Full Text Search" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:36 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:55 msgid "Prompt" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:38 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:57 msgid "Text" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:44 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:63 #: horizon/static/framework/widgets/transfer-table/transfer-table.example.html:14 msgid "Name" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:49 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:74 msgid "Status" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:51 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:76 msgid "Active" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:52 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:77 msgid "Shutdown" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:53 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:78 #: horizon/static/framework/widgets/toast/toast.service.js:62 msgid "Error" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:58 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:83 msgid "Flavor" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:61 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:86 msgid "m1.tiny" msgstr "" #: horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js:62 #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:87 msgid "m1.small" msgstr "" #: horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js:68 msgid "Server Name" msgstr "" #: horizon/static/framework/widgets/metadata/display/metadata-display.html:25 msgid "Detail Information" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:45 msgid "" "You can specify resource metadata by moving items from the left column to" " the right column. In the left column there are metadata definitions from" " the Glance Metadata Catalog. Use the \"Custom\" option to add metadata " "with the key of your choice." msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:47 msgid "Min" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:49 msgid "Min length" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:50 msgid "Max length" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:51 msgid "Pattern mismatch" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:52 msgid "Integer required" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:53 msgid "Decimal required" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:54 msgid "Required" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:55 msgid "Duplicate keys are not allowed" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:56 #: horizon/static/framework/widgets/table/table.module.js:50 msgid "Filter" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:57 msgid "Available Metadata" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:58 msgid "Existing Metadata" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:59 msgid "Custom" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:60 msgid "No available metadata" msgstr "" #: horizon/static/framework/widgets/metadata/tree/tree.module.js:61 msgid "No existing metadata" msgstr "" #: horizon/static/framework/widgets/modal/delete-modal.service.spec.js:20 msgid "Confirm Delete Foobars" msgstr "" #: horizon/static/framework/widgets/modal/delete-modal.service.spec.js:21 #, python-format msgid "selected \"%s\"" msgstr "" #: horizon/static/framework/widgets/modal/delete-modal.service.spec.js:22 #: horizon/static/horizon/js/horizon.networktopology.js:963 msgid "Delete" msgstr "" #: horizon/static/framework/widgets/modal/delete-modal.service.spec.js:23 #, python-format msgid "Deleted : %s." msgstr "" #: horizon/static/framework/widgets/modal/delete-modal.service.spec.js:24 #, python-format msgid "Unable to delete: %s." msgstr "" #: horizon/static/framework/widgets/modal/simple-modal.service.js:81 msgid "Submit" msgstr "" #: horizon/static/framework/widgets/table/hz-no-items.html:5 msgid "{$ message || 'No items to display.' $}" msgstr "" #: horizon/static/framework/widgets/toast/toast.service.js:58 msgid "Danger" msgstr "" #: horizon/static/framework/widgets/toast/toast.service.js:59 msgid "Warning" msgstr "" #: horizon/static/framework/widgets/toast/toast.service.js:60 msgid "Info" msgstr "" #: horizon/static/framework/widgets/toast/toast.service.js:61 msgid "Success" msgstr "" #: horizon/static/framework/widgets/transfer-table/transfer-table.example.html:15 msgid "description" msgstr "" #: horizon/static/framework/widgets/transfer-table/transfer-table.example.html:23 #: horizon/static/framework/widgets/transfer-table/transfer-table.module.js:60 msgid "No available items" msgstr "" #: horizon/static/framework/widgets/transfer-table/transfer-table.module.js:56 msgid "Allocated" msgstr "" #: horizon/static/framework/widgets/transfer-table/transfer-table.module.js:57 msgid "Available" msgstr "" #: horizon/static/framework/widgets/transfer-table/transfer-table.module.js:58 msgid "Select one" msgstr "" #: horizon/static/framework/widgets/transfer-table/transfer-table.module.js:59 msgid "Select an item from Available items below" msgstr "" #: horizon/static/framework/widgets/transfer-table/transfer-table.module.js:61 msgid "Expand to see allocated items" msgstr "" #: horizon/static/framework/widgets/transfer-table/transfer-table.module.js:62 msgid "Expand to see available items" msgstr "" #: horizon/static/framework/widgets/transfer-table/transfer-table.module.js:63 msgid "Click to show or hide" msgstr "" #: horizon/static/framework/widgets/transfer-table/transfer-table.module.js:64 msgid "Re-order items using drag and drop" msgstr "" #: horizon/static/framework/widgets/transfer-table/transfer-table.module.js:65 msgid "Click to see more details" msgstr "" #: horizon/static/framework/widgets/wizard/wizard.html:15 msgid "Toggle navigation" msgstr "" #: horizon/static/framework/widgets/wizard/wizard.module.js:48 msgid "Back" msgstr "" #: horizon/static/framework/widgets/wizard/wizard.module.js:49 msgid "Next" msgstr "" #: horizon/static/framework/widgets/wizard/wizard.module.js:50 msgid "Finish" msgstr "" #: horizon/static/horizon/js/horizon.d3linechart.js:395 #: horizon/static/horizon/js/horizon.d3linechart.js:405 msgid "No data available." msgstr "" #: horizon/static/horizon/js/horizon.d3linechart.js:411 #: horizon/static/horizon/js/horizon.modals.js:348 #: horizon/static/horizon/js/horizon.tables_inline_edit.js:92 #: horizon/static/horizon/js/horizon.tables_inline_edit.js:155 msgid "An error occurred. Please try again later." msgstr "" #: horizon/static/horizon/js/horizon.firewalls.js:32 #: horizon/static/horizon/js/horizon.instances.js:32 msgid "There was a problem communicating with the server, please try again." msgstr "" #: horizon/static/horizon/js/horizon.forms.js:98 msgid "pool default" msgstr "" #: horizon/static/horizon/js/horizon.instances.js:274 msgid "Could not read the file" msgstr "" #: horizon/static/horizon/js/horizon.instances.js:280 #: horizon/static/horizon/js/horizon.instances.js:309 msgid "Could not decrypt the password" msgstr "" #: horizon/static/horizon/js/horizon.membership.js:185 msgid "No roles" msgstr "" #: horizon/static/horizon/js/horizon.membership.js:217 msgid "Roles" msgstr "" #: horizon/static/horizon/js/horizon.messages.js:9 msgid "Danger: " msgstr "" #: horizon/static/horizon/js/horizon.messages.js:10 msgid "Warning: " msgstr "" #: horizon/static/horizon/js/horizon.messages.js:11 msgid "Notice: " msgstr "" #: horizon/static/horizon/js/horizon.messages.js:12 msgid "Success: " msgstr "" #: horizon/static/horizon/js/horizon.messages.js:13 msgid "Error: " msgstr "" #: horizon/static/horizon/js/horizon.modals.js:238 #: horizon/static/horizon/js/horizon.tables.js:278 msgid "Working" msgstr "" #: horizon/static/horizon/js/horizon.modals.js:275 msgid "There was an error submitting the form. Please try again." msgstr "" #: horizon/static/horizon/js/horizon.modals.js:329 #: horizon/static/horizon/js/horizon.modals.js:412 #: horizon/static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:966 msgid "STATUS" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:967 msgid "ID" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:968 msgid "Interfaces" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:969 msgid "Subnets" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:970 msgid "Delete Interface" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:971 msgid "Delete Subnet" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:972 msgid "Open Console" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:973 msgid "View Details" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:974 msgid "IP Addresses" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:978 msgid "Delete Router" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:979 msgid "View Router Details" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:982 msgid "Add Interface" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:988 msgid "Delete Instance" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:989 msgid "View Instance Details" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:1003 msgid "Delete Network" msgstr "" #: horizon/static/horizon/js/horizon.networktopology.js:1006 msgid "Create Subnet" msgstr "" #: horizon/static/horizon/js/horizon.tables.js:45 #: horizon/static/horizon/js/horizon.tables.js:448 msgid "No items to display." msgstr "" #: horizon/static/horizon/js/horizon.tables.js:58 #: horizon/static/horizon/js/horizon.tables.js:143 msgid "An error occurred while updating." msgstr "" #: horizon/static/horizon/js/horizon.tables.js:257 #, python-format msgid "You have selected %s. " msgstr "" #: horizon/static/horizon/js/horizon.tables.js:259 #, python-format msgid "Confirm %s" msgstr "" #: horizon/static/horizon/js/horizon.tables.js:260 msgid "Please confirm your selection. " msgstr "" #: horizon/static/horizon/js/horizon.tables_inline_edit.js:86 #: horizon/static/horizon/js/horizon.tables_inline_edit.js:149 msgid "Not authorized to do this operation." msgstr "" #: horizon/static/horizon/js/horizon.users.js:18 msgid "Passwords do not match." msgstr "" #: horizon/static/horizon/js/horizon.volumes.js:43 msgid "No description available." msgstr "" #: horizon/static/horizon/js/angular/directives/serialConsole.js:27 msgid "Connecting" msgstr "" #: horizon/static/horizon/js/angular/directives/serialConsole.js:28 msgid "Open" msgstr "" #: horizon/static/horizon/js/angular/directives/serialConsole.js:29 msgid "Closing" msgstr "" #: horizon/static/horizon/js/angular/directives/serialConsole.js:30 msgid "Closed" msgstr "" #: horizon/static/horizon/js/angular/directives/serialConsole.js:97 #, python-format msgid "Status: %s" msgstr "" horizon-9.0.0/horizon/locale/zh_CN/0000775000567000056710000000000012701407231020276 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/zh_CN/LC_MESSAGES/0000775000567000056710000000000012701407231022063 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/zh_CN/LC_MESSAGES/django.po0000664000567000056710000002170212701407071023671 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata # zhangjingwen , 2015. #zanata # Zheng Xi Zhou , 2016. #zanata # liujunpeng , 2016. #zanata # vuuv , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-23 12:46+0000\n" "Last-Translator: vuuv \n" "Language-Team: Chinese (China)\n" "Language: zh-CN\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " 使用其他用户登录或返回 首页\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " 已用 %(used)s (æ— é™åˆ¶)\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " 已用 %(used)s , å¯ç”¨ %(available)s \n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" "%(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" "如果你ä¸ç¡®å®šä½¿ç”¨å“ªç§è®¤è¯æ–¹å¼ï¼Œè¯·è”系管ç†å‘˜ã€‚\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d 字节" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s æˆåŠŸå®Œæˆã€‚" #, python-format msgid "%s did not complete." msgstr "%s 没有完æˆã€‚" msgid "« Prev" msgstr "« å‰é¡µ" msgid "(No Limit)" msgstr "(æ— é™åˆ¶)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 字节" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "å·²ç»å­˜åœ¨å为 \"%(name)s\" çš„ %(resource)s。" msgid "Actions" msgstr "动作" msgid "Active Instances:" msgstr "è¿è¡Œçš„云主机:" msgid "Active RAM:" msgstr "å¯ç”¨çš„内存:" msgid "Add a row" msgstr "增加一行" msgid "All available" msgstr "所有å¯ç”¨æˆå‘˜" msgid "Available" msgstr "å¯ç”¨é…é¢" msgid "Back" msgstr "返回" #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "Batch Item" #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "Batched Item" msgid "Cancel" msgstr "å–æ¶ˆ" msgid "Connect" msgstr "连接" msgid "Delete" msgstr "删除" msgid "Deleted" msgstr "已删除" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "正在显示 %(content_items)s 项" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "正在显示 %(counter)s 项" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "正在显示 %(nav_items)s 项" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "Down Item" #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "Downed Item" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "处ç†jsonæ¶ˆæ¯æ–‡ä»¶ '%(path)s' 时出错: %(exception)s" msgid "Error: " msgstr "错误:" msgid "Fake" msgstr "Fake" msgid "Filter" msgstr "筛选" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "IPåœ°å€æ ¼å¼ä¸å¯¹" msgid "Info: " msgstr "ä¿¡æ¯ï¼š" msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "æ— æ•ˆçš„å…ƒæ•°æ®æ¡ç›®ï¼Œè¯·ä½¿ç”¨é€—å·åˆ†éš”çš„ key=value 组åˆ" msgid "Invalid subnet mask" msgstr "无效的å­ç½‘掩ç " msgid "Invalid version for IP address" msgstr "无效的 IP 地å€ç‰ˆæœ¬" msgid "Limit Summary" msgstr "ä¸Šé™æ‘˜è¦" msgid "Log in" msgstr "登录" msgid "Login" msgstr "登录" msgid "Members" msgstr "æˆå‘˜" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "json æ¶ˆæ¯æ–‡ä»¶ '%(path)s' 内容格å¼ä¸å¯¹: %(exception)s" msgid "More Actions" msgstr "更多æ“作" msgid "Navigation Item" msgstr "导航æ¡ç›®" msgid "Never" msgstr "从ä¸" msgid "Next" msgstr "下一步" msgid "Next »" msgstr "åŽé¡µ »" msgid "No items to display." msgstr "æ²¡æœ‰è¦æ˜¾ç¤ºçš„æ¡ç›®ã€‚" #, python-format msgid "No match returned for the id \"%s\"." msgstr "id为 \"%s\" 的对象没有匹é…项。" msgid "No members." msgstr "没有æˆå‘˜ã€‚" msgid "None available." msgstr "没有å¯ç”¨æˆå‘˜ã€‚" msgid "Not a valid IP protocol number" msgstr "䏿˜¯ä¸€ä¸ªæœ‰æ•ˆçš„IPåè®®å·" msgid "Not a valid port number" msgstr "䏿˜¯ä¸€ä¸ªæœ‰æ•ˆçš„端å£å·" msgid "One colon allowed in port range" msgstr "端å£èŒƒå›´åªèƒ½ä½¿ç”¨ä¸€ä¸ªå†’å·" msgid "Other" msgstr "其它" msgid "Password is not accepted" msgstr "密ç ä¸ç¬¦åˆè¦æ±‚" msgid "Please log in to continue." msgstr "请先登录。" msgid "Please select a row before taking that action." msgstr "请在执行å‰é€‰æ‹©ä¸€è¡Œã€‚" msgid "Processing..." msgstr "正在处ç†..." msgid "Save" msgstr "ä¿å­˜" #, python-format msgid "Select a %s to browse." msgstr "è¯·é€‰æ‹©è¦æµè§ˆçš„ %s 。" msgid "Select a period of time to query its usage:" msgstr "é€‰æ‹©ä¸€æ®µæ—¶é—´æ¥æŸ¥è¯¢å…¶ç”¨é‡ï¼š" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Sell Puppy" msgid "Sign In" msgstr "登入" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Sold Puppy" msgid "Submit" msgstr "æäº¤" msgid "Success: " msgstr "æˆåŠŸï¼š" msgid "Summary" msgstr "概è¦" #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "%(obj)s ä¸å­˜åœ¨ %(attr)s 属性。" msgid "The date should be in YYYY-mm-dd format." msgstr "日期应是YYYY-mm-ddæ ¼å¼ã€‚" msgid "The string may only contain ASCII printable characters." msgstr "字符串åªèƒ½åŒ…å« ASCII 坿˜¾ç¤ºï¼ˆprintable)字符." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "在模æ¿ä¸­ï¼Œ%(resource)s 的值是%(name)s 。当从这个接å£å¯åŠ¨æ ˆæ—¶ï¼Œå€¼å¿…é¡»ä»¥" "\"http://\" 或\"https://\"开头。" msgid "This Period's GB-Hours:" msgstr "周期内ç£ç›˜GB-å°æ—¶æ•°:" msgid "This Period's RAM-Hours:" msgstr "周期内内存-å°æ—¶æ•°" msgid "This Period's VCPU-Hours:" msgstr "周期内VCPU-å°æ—¶æ•°:" msgid "This action cannot be undone." msgstr "这个动作ä¸èƒ½æ’¤æ¶ˆã€‚" #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "无法执行 %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "未授æƒã€‚请å°è¯•釿–°ç™»å½•。" #, python-format msgid "Unauthorized: %s" msgstr "未授æƒï¼š%s" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "Up Item" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "æ›´æ–°æ¡ç›®" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "æ¡ç›®å·²æ›´æ–°" #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "Upped Item" msgid "Usage Summary" msgstr "使用情况摘è¦" msgid "Warning: " msgstr "警告:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "æ‚¨è¢«ç¦æ­¢æ‰§è¡Œ %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "您无æƒè®¿é—® %s" msgid "You do not have permission to access the resource:" msgstr "你没有被授æƒè®¿é—®è¿™äº›èµ„æºï¼š" horizon-9.0.0/horizon/locale/zh_CN/LC_MESSAGES/djangojs.po0000664000567000056710000002020212701407071024220 0ustar jenkinsjenkins00000000000000# Daisy , 2015. #zanata # OpenStack Infra , 2015. #zanata # Yu Zhiuguo , 2015. #zanata # yan haifeng , 2015. #zanata # zhangjingwen , 2015. #zanata # Zheng Xi Zhou , 2016. #zanata # liujunpeng , 2016. #zanata # qingszhao , 2016. #zanata # vuuv , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-23 02:16+0000\n" "Last-Translator: vuuv \n" "Language-Team: Chinese (China)\n" "Language: zh-CN\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s 字节" msgid "(Modified)" msgstr "(已修改)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "è¿è¡Œä¸­" msgid "Add Interface" msgstr "增加接å£" msgid "Added" msgstr "已添加" msgid "Allocated" msgstr "已分é…" msgid "An error occurred while updating." msgstr "æ›´æ–°æ—¶å‘生错误。" msgid "An error occurred. Please try again later." msgstr "å‘生错误。请ç¨åŽé‡è¯•。" msgid "Available" msgstr "å¯ç”¨é…é¢" msgid "Available Metadata" msgstr "å¯ç”¨çš„元数æ®" msgid "Back" msgstr "返回" msgid "Cancel" msgstr "å–æ¶ˆ" msgid "Click here for filters." msgstr "点击这里过滤" msgid "Click here to expand the row and view the errors." msgstr "点击此处展开,并坿µè§ˆé”™è¯¯ã€‚" msgid "Click to see more details" msgstr "点击查看更多细节" msgid "Click to show or hide" msgstr "点击此处展现或éšè—" msgid "Closed" msgstr "已关闭" msgid "Closing" msgstr "关闭中" #, python-format msgid "Confirm %s" msgstr "确认 %s" msgid "Confirm Delete Foobars" msgstr "确认删除Foobars" msgid "Connecting" msgstr "连接中" msgid "Could not decrypt the password" msgstr "ä¸èƒ½è§£å¯†å¯†ç " msgid "Could not read the file" msgstr "ä¸èƒ½è¯»å–文件" msgid "Create Subnet" msgstr "创建å­ç½‘" msgid "Current Usage" msgstr "当å‰ç”¨é‡" msgid "Custom" msgstr "定制" msgid "Customization Script" msgstr "自定义脚本" msgid "Danger" msgstr "å±é™©" msgid "Danger: " msgstr "å±é™©ï¼š" msgid "Decimal required" msgstr "å¿…é¡»æ˜¯å°æ•°" msgid "Delete" msgstr "删除" msgid "Delete Instance" msgstr "删除云主机" msgid "Delete Interface" msgstr "删除接å£" msgid "Delete Network" msgstr "删除网络" msgid "Delete Router" msgstr "删除路由" msgid "Delete Subnet" msgstr "删除å­ç½‘" #, python-format msgid "Deleted : %s." msgstr "删除:%s。" msgid "Detail Information" msgstr "详细信æ¯" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr " 正在显示 %(total)s 项中的第 %(count)s 项" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "正在显示 %s 项" msgid "Duplicate keys are not allowed" msgstr "å¤åˆ¶å¯†é’¥æ˜¯ä¸å…许的" msgid "Error" msgstr "错误" msgid "Error: " msgstr "错误:" msgid "Example" msgstr "例å­" msgid "Existing Metadata" msgstr "已存在的元数æ®" msgid "Expand to see allocated items" msgstr "展开å¯è§å·²åˆ†é…项" msgid "Expand to see available items" msgstr "展开å¯è§å¯é€‰é¡¹" msgid "Filter" msgstr "筛选" msgid "Finish" msgstr "结æŸ" msgid "Flavor" msgstr "云主机类型" msgid "Full Text Search" msgstr "全文æœç´¢" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "IP地å€" msgid "Info" msgstr "ä¿¡æ¯" msgid "Integer required" msgstr "必须是整数" msgid "Interfaces" msgstr "接å£" msgid "Load script from a file" msgstr "从文件载入脚本" msgid "Loading" msgstr "加载中" msgid "Max" msgstr "最大" msgid "Max length" msgstr "最大长度" msgid "Min" msgstr "最å°" msgid "Min length" msgstr "最å°é•¿åº¦" msgid "Name" msgstr "åç§°" msgid "Next" msgstr "下一步" msgid "No" msgstr "ä¸" msgid "No Limit" msgstr "æ— é™åˆ¶" msgid "No available items" msgstr "没有å¯é€‰é¡¹" msgid "No available metadata" msgstr "没有å¯ç”¨çš„元数æ®" msgid "No data available." msgstr "没有å¯ç”¨æ•°æ®" msgid "No description available." msgstr "没有å¯ç”¨çš„æè¿°ã€‚" msgid "No existing metadata" msgstr "ä¸å­˜åœ¨çš„元数æ®" msgid "No items to display." msgstr "没有æ¡ç›®æ˜¾ç¤ºã€‚" msgid "No roles" msgstr "ä¸å­˜åœ¨ä»»ä½•角色" msgid "None" msgstr "æ— " msgid "Not authorized to do this operation." msgstr "未授æƒä¸èƒ½è¿›è¡Œæ­¤æ“作。" msgid "Notice: " msgstr "注æ„:" msgid "Open" msgstr "打开" msgid "Open Console" msgstr "打开控制å°" msgid "Passwords do not match." msgstr "密ç ä¸åŒ¹é…。" msgid "Pattern mismatch" msgstr "模å¼ä¸åŒ¹é…" msgid "Please confirm your selection. " msgstr "请确认您的选择。" msgid "Prompt" msgstr "æç¤º" msgid "Re-order items using drag and drop" msgstr "ä½¿ç”¨æ‹–æ”¾é‡æ–°æŽ’åº" msgid "Remaining" msgstr "剩余é‡" msgid "Remove" msgstr "移除" msgid "Required" msgstr "å¿…é¡»" msgid "Roles" msgstr "角色" msgid "STATUS" msgstr "状æ€" #. Strings between {$ and $} should be left untranslated. msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgstr "" "脚本大å°: {$ (scriptLength || 0) | bytes $} / {$ config.MAX_SCRIPT_SIZE | " "bytes $} (最大)" msgid "Search in current results" msgstr "在当å‰ç»“果中æœç´¢" msgid "Select an item from Available items below" msgstr "从以下å¯é€‰é¡¹ä¸­é€‰æ‹©ä¸€é¡¹" msgid "Select one" msgstr "选择一个" msgid "Server Name" msgstr "æœåС噍å" msgid "Shutdown" msgstr "关闭" msgid "Status" msgstr "状æ€" #, python-format msgid "Status: %s" msgstr "状æ€: %s" msgid "Submit" msgstr "æäº¤" msgid "Subnets" msgstr "å­ç½‘" msgid "Success" msgstr "æˆåŠŸ" msgid "Success: " msgstr "æˆåŠŸï¼š" msgid "Text" msgstr "文本" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "ä¸èƒ½æ‰§è¡Œè¯¥æ“作。该行的内容有错或是缺少信æ¯ã€‚" msgid "The script is larger than the maximum size" msgstr "脚本大å°è¶…出最大值" msgid "There was a problem communicating with the server, please try again." msgstr "与æœåŠ¡å™¨é€šä¿¡å‡ºçŽ°é—®é¢˜ï¼Œè¯·å†è¯•一次。" msgid "There was an error submitting the form. Please try again." msgstr "在æäº¤è¡¨å•çš„æ—¶å€™å‡ºçŽ°é”™è¯¯ï¼Œè¯·å†æ¬¡å°è¯•。" msgid "Toggle Dropdown" msgstr "切æ¢ä¸‹æ‹‰èœå•" msgid "Toggle navigation" msgstr "切æ¢å¯¼èˆª" msgid "Total" msgstr "总共" #, python-format msgid "Unable to delete: %s." msgstr "无法删除:%s。" msgid "Unlimited" msgstr "æ— é™åˆ¶çš„" msgid "View Details" msgstr "查看详情" msgid "View Instance Details" msgstr "查看云主机详情" msgid "View Router Details" msgstr "查看路由详情" msgid "Warning" msgstr "警告" msgid "Warning: " msgstr "警告:" msgid "Working" msgstr "进行中" msgid "Yes" msgstr "是" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "ä½ å¯ä»¥é€šè¿‡æŠŠå·¦ä¾§çš„æ¡ç›®ç§»åˆ°å³ä¾§æ¥æŒ‡å®šèµ„æºçš„元数æ®ã€‚左侧是Glance元数æ®ç›®å½•" "(Metadata Catalog)里的元数æ®å®šä¹‰ã€‚使用\"自定义\"选项æ¥å¢žåŠ å…ƒæ•°æ®ã€‚" #, python-format msgid "You have selected %s. " msgstr "ä½ å·²ç»é€‰æ‹©äº† %s 。 " msgid "description" msgstr "æè¿°" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "é»˜è®¤èµ„æºæ± " #, python-format msgid "selected \"%s\"" msgstr "选择 \"%s\"" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || 'æ²¡æœ‰å¯æ˜¾ç¤ºçš„æ¡ç›®.' $}" horizon-9.0.0/horizon/locale/es/0000775000567000056710000000000012701407231017704 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/es/LC_MESSAGES/0000775000567000056710000000000012701407231021471 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/es/LC_MESSAGES/django.po0000664000567000056710000002125412701407063023302 0ustar jenkinsjenkins00000000000000# Marian Tort , 2015. #zanata # OpenStack Infra , 2015. #zanata # Alberto Molina Coballes , 2016. #zanata # Pablo Iranzo Gómez , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-02-19 12:25+0000\n" "Last-Translator: Pablo Iranzo Gómez \n" "Language-Team: Spanish\n" "Language: es\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " Conectarse como otro usuario o volver atrás a la página principal\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " Usados %(used)s (Ilimitado)\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " Usados %(used)s de %(available)s \n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" "Si no está seguro del método de autenticación a utilizar, contacte con su " "administrador.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d bite" msgstr[1] "%(size)d bites" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s completado correctamente." #, python-format msgid "%s did not complete." msgstr "%s no completado." msgid "« Prev" msgstr "« Prev" msgid "(No Limit)" msgstr "(Sin Límite)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 Bytes" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Un %(resource)s con el nombre \"%(name)s\" ya existe." msgid "Actions" msgstr "Acciones" msgid "Active Instances:" msgstr "Instancias activas:" msgid "Active RAM:" msgstr "RAM activa:" msgid "Add a row" msgstr "Agregar una fila" msgid "All available" msgstr "Todos los disponibles" msgid "Available" msgstr "Disponible" msgid "Back" msgstr "Anterior" msgid "Cancel" msgstr "Cancelar " msgid "Connect" msgstr "Conectar" msgid "Delete" msgstr "Eliminar" msgid "Deleted" msgstr "Eliminado" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Mostrando %(content_items)s articulo" msgstr[1] "Mostrando %(content_items)s articulos" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Mostrando %(counter)s articulo" msgstr[1] "Mostrando %(counter)s articulos" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Mostrando %(nav_items)s articulo" msgstr[1] "Mostrando %(nav_items)s articulos" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "Error procesando el fichero json de mensajes '%(path)s': %(exception)s" msgid "Error: " msgstr "Error: " msgid "Fake" msgstr "Falso" msgid "Filter" msgstr "Filtrar" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Formato de dirección IP incorrecto" msgid "Info: " msgstr "Info:" msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "Entrada de metadata inválida, pares clave=valor separados por coma" msgid "Invalid subnet mask" msgstr "Máscara de red no válida" msgid "Invalid version for IP address" msgstr "Versión de dirección IP no válida" msgid "Limit Summary" msgstr "Resumen" msgid "Log in" msgstr "Conectarse" msgid "Login" msgstr "Usuario" msgid "Members" msgstr "Miembros" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "El fichero json de mensajes '%(path)s' es incorrecto. %(exception)s" msgid "More Actions" msgstr "Más acciones" msgid "Navigation Item" msgstr "Elemento de navegación" msgid "Never" msgstr "Nunca" msgid "Next" msgstr "Siguiente" msgid "Next »" msgstr "Siguiente »" msgid "No items to display." msgstr "No hay ítems que mostrar." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Ninguna coincidencia para el id \"%s\"." msgid "No members." msgstr "Sin miembros." msgid "None available." msgstr "Ninguno disponible." msgid "Not a valid IP protocol number" msgstr "Número de protocolo IP no válido" msgid "Not a valid port number" msgstr "Número de puerto no válido" msgid "One colon allowed in port range" msgstr "Está permitido usar dos puntos en el rango de puerto" msgid "Other" msgstr "Otro" msgid "Password is not accepted" msgstr "La contraseña no se ha aceptado" msgid "Please log in to continue." msgstr "Inicie sesión para continuar." msgid "Please select a row before taking that action." msgstr "Seleccione una fila antes de realizar la acción." msgid "Processing..." msgstr "Procesando..." msgid "Save" msgstr "Guardar" #, python-format msgid "Select a %s to browse." msgstr "Seleccione una %s para navegar." msgid "Select a period of time to query its usage:" msgstr "Seleccione un periodo de tiempo para consultar su uso: " #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Vender Mascota" msgstr[1] "Vender Mascotas" msgid "Sign In" msgstr "Iniciar sesión" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Mascota Vendida" msgstr[1] "Mascotas Vendidas" msgid "Submit" msgstr "Enviar" msgid "Success: " msgstr "Correcto:" msgid "Summary" msgstr "Resumen" msgid "The date should be in YYYY-mm-dd format." msgstr "La fecha debe estar en formato AAAA-MM-DD." msgid "The string may only contain ASCII printable characters." msgstr "La cadena sólo puede incluir caracteres imprimibles ASCII." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "El valor de %(resource)s es %(name)s dentro de la plantilla. Al lanzar un " "stack desde este interfaz, el valor debe comenzar por \"http://\" o " "\"https://\"" msgid "This Period's GB-Hours:" msgstr "Este periodo en horas GB:" msgid "This Period's RAM-Hours:" msgstr "Horas-RAM de este periodo:" msgid "This Period's VCPU-Hours:" msgstr "Este periodo en horas VCPU:" msgid "This action cannot be undone." msgstr "No se puede deshacer esta acción." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "No ha sido posible %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "No autorizado. Inicie sesión de nuevo." #, python-format msgid "Unauthorized: %s" msgstr "No autorizado: %s" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "Actualizar elemento" msgstr[1] "Actualizar elementos" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "Elemento actualizado" msgstr[1] "Elementos actualizados" msgid "Usage Summary" msgstr "Resumen del uso" msgid "Warning: " msgstr "Advertencia:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "No le está permitido %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "No está autorizado para acceder a %s" msgid "You do not have permission to access the resource:" msgstr "No tiene permisos para acceder al recurso:" horizon-9.0.0/horizon/locale/es/LC_MESSAGES/djangojs.po0000664000567000056710000001673512701407063023647 0ustar jenkinsjenkins00000000000000# Eduardo Gonzalez Gutierrez , 2015. #zanata # OpenStack Infra , 2015. #zanata # Pablo Iranzo Gómez , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-01-18 02:54+0000\n" "Last-Translator: Pablo Iranzo Gómez \n" "Language-Team: Spanish\n" "Language: es\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s bytes" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "Activo" msgid "Add Interface" msgstr "Añadir interfaz" msgid "Added" msgstr "Añadido" msgid "Allocated" msgstr "Asignados" msgid "An error occurred while updating." msgstr "Ha ocurrido un error durante la actualización." msgid "An error occurred. Please try again later." msgstr "Ha ocurrido un error. Inténtelo de nuevo más tarde." msgid "Available" msgstr "Disponible" msgid "Available Metadata" msgstr "Metadatos disponibles" msgid "Back" msgstr "Anterior" msgid "Cancel" msgstr "Cancelar " msgid "Click here for filters." msgstr "Pulse aquí para filtros." msgid "Click here to expand the row and view the errors." msgstr "Haga click aquí para ver los errores." msgid "Click to see more details" msgstr "Haga click para ver más detalles" msgid "Click to show or hide" msgstr "Haga click para mostrar u ocultar" msgid "Closed" msgstr "Cerrada" msgid "Closing" msgstr "Cerrando" #, python-format msgid "Confirm %s" msgstr "Confirmar %s" msgid "Connecting" msgstr "Conectando" msgid "Could not decrypt the password" msgstr "No se ha podido descifrar la contraseña" msgid "Could not read the file" msgstr "No se ha podido leer el fichero" msgid "Create Subnet" msgstr "Crear Subred" msgid "Current Usage" msgstr "Uso actual" msgid "Custom" msgstr "Personalizar" msgid "Danger" msgstr "Peligro" msgid "Danger: " msgstr "Peligro:" msgid "Decimal required" msgstr "Decimal obligatorio" msgid "Delete" msgstr "Eliminar" msgid "Delete Instance" msgstr "Eliminar instancia" msgid "Delete Interface" msgstr "Eliminar interfaz" msgid "Delete Network" msgstr "Borrar Red" msgid "Delete Router" msgstr "Eliminar router" msgid "Delete Subnet" msgstr "Borrar Subred" #, python-format msgid "Deleted : %s." msgstr "Eliminado : %s." msgid "Detail Information" msgstr "Información detallada" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "Mostrando %(count)s de %(total)s elementos" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "Mostrando %s articulo" msgstr[1] "Mostrando %s articulos" msgid "Duplicate keys are not allowed" msgstr "Las claves duplicadas no están permitidas" msgid "Error" msgstr "Error" msgid "Error: " msgstr "Error: " msgid "Existing Metadata" msgstr "Metadatos existentes" msgid "Expand to see allocated items" msgstr "Expandir para ver los ítems asociados" msgid "Expand to see available items" msgstr "Expandir para ver los ítems disponibles" msgid "Filter" msgstr "Filtrar" msgid "Finish" msgstr "Finalizar" msgid "Flavor" msgstr "Sabor" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "Direcciones IP" msgid "Info" msgstr "Info" msgid "Integer required" msgstr "Entero obligatorio" msgid "Interfaces" msgstr "Interfaces" msgid "Loading" msgstr "Cargando" msgid "Max" msgstr "Máx." msgid "Max length" msgstr "Longitud máx." msgid "Min" msgstr "Mín." msgid "Min length" msgstr "Longitud mín." msgid "Name" msgstr "Nombre" msgid "Next" msgstr "Siguiente" msgid "No" msgstr "No" msgid "No available items" msgstr "No hay ítems disponibles" msgid "No available metadata" msgstr "Metadatos no disponibles" msgid "No data available." msgstr "No hay datos disponibles." msgid "No description available." msgstr "Descripción no disponible." msgid "No existing metadata" msgstr "No hay metadatos existentes" msgid "No items to display." msgstr "No hay ítems que mostrar." msgid "No roles" msgstr "Sin roles" msgid "None" msgstr "Ninguno" msgid "Not authorized to do this operation." msgstr "No tiene autorización para realizar esta operación." msgid "Notice: " msgstr "Aviso:" msgid "Open" msgstr "Abierta" msgid "Open Console" msgstr "Abrir consola" msgid "Passwords do not match." msgstr "Las contraseñas no coinciden." msgid "Pattern mismatch" msgstr "Discrepancia en el patrón" msgid "Please confirm your selection. " msgstr "Confirme su selección." msgid "Prompt" msgstr "Inmediato" msgid "Re-order items using drag and drop" msgstr "Reordene los ítems arrastrando y soltando." msgid "Remaining" msgstr "Restante " msgid "Remove" msgstr "Eliminar" msgid "Required" msgstr "Obligatorio" msgid "Roles" msgstr "Roles" msgid "STATUS" msgstr "ESTADO" msgid "Select an item from Available items below" msgstr "Seleccione un ítem de los disponibles abajo" msgid "Select one" msgstr "Seleccione uno" msgid "Server Name" msgstr "Nombre del servidor" msgid "Shutdown" msgstr "Apagar" msgid "Status" msgstr "Estado" #, python-format msgid "Status: %s" msgstr "Estado: %s" msgid "Submit" msgstr "Enviar" msgid "Subnets" msgstr "Subredes" msgid "Success" msgstr "Éxito" msgid "Success: " msgstr "Correcto:" msgid "Text" msgstr "Texto" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "Esta acción no se puede llevar a cabo. Esta columna contiene errores o " "carece de información." msgid "There was a problem communicating with the server, please try again." msgstr "" "Ha ocurrido un problema en la comunicación con el servidor, inténtelo de " "nuevo." msgid "There was an error submitting the form. Please try again." msgstr "Ha ocurrido un error al enviar el formulario. Inténtelo de nuevo." msgid "Toggle Dropdown" msgstr "Alternar desplegable" msgid "Total" msgstr "Total" #, python-format msgid "Unable to delete: %s." msgstr "No fue posible eliminar: %s." msgid "View Details" msgstr "Ver detalles" msgid "View Instance Details" msgstr "Ver detalles de la instancia" msgid "View Router Details" msgstr "Ver detalles del router" msgid "Warning" msgstr "Advertencia:" msgid "Warning: " msgstr "Advertencia:" msgid "Working" msgstr "Trabajando" msgid "Yes" msgstr "Sí" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "Puede especificar los metadatos para los recursos moviendo elementos de la " "columna izquierda a la derecha. En la columna izquierda aparecen las " "definiciones de metadatos del catálogo de Glance. Utilice la opción " "\"Personalizado\" para añadir metadatos con los valores que escoja." #, python-format msgid "You have selected %s. " msgstr "Ha seleccionado %s." msgid "description" msgstr "descripción" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "Predeterminados del pool" #, python-format msgid "selected \"%s\"" msgstr "seleccionado \"%s\"" horizon-9.0.0/horizon/locale/sl_SI/0000775000567000056710000000000012701407231020306 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/sl_SI/LC_MESSAGES/0000775000567000056710000000000012701407231022073 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/sl_SI/LC_MESSAGES/django.po0000664000567000056710000001117012701407063023700 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Slovenian (Slovenia)\n" "Language: sl-SI\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n" "%100==4 ? 3 : 0)\n" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" "ÄŒe niste prepriÄani, katero metodo uporabiti za avtentikacijo, se obrnite na " "skrbnika storitve." #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s se je uspeÅ¡no izvedel/a." #, python-format msgid "%s did not complete." msgstr "%s ni bil/a uspeÅ¡no dokonÄan/a" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 bajtov" msgid "Actions" msgstr "Operacije" msgid "Active Instances:" msgstr "Aktivne instance:" msgid "Active RAM:" msgstr "Uporabljen RAM" msgid "Add a row" msgstr "Dodaj vrstico" msgid "All available" msgstr "Vsi na voljo" msgid "Available" msgstr "Na voljo" msgid "Back" msgstr "Nazaj" msgid "Cancel" msgstr "PrekliÄi" msgid "Connect" msgstr "Poveži" msgid "Delete" msgstr "IzbriÅ¡i" msgid "Deleted" msgstr "Izbrisano" msgid "Error: " msgstr "Napaka:" msgid "Filter" msgstr "Filter" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Neveljaven zapis IP-naslova" msgid "Info: " msgstr "Informacije:" msgid "Invalid subnet mask" msgstr "Neveljavna maska podomrežja" msgid "Invalid version for IP address" msgstr "Neveljavna razliÄica IP-naslova" msgid "Limit Summary" msgstr "Povzetek omejitev" msgid "Login" msgstr "Prijava" msgid "Members" msgstr "ÄŒlani" msgid "More Actions" msgstr "VeÄ operacij" msgid "Navigation Item" msgstr "Navigacijska postavka" msgid "Never" msgstr "Nikoli" msgid "Next" msgstr "Naprej" msgid "No items to display." msgstr "Ni zadetkov" #, python-format msgid "No match returned for the id \"%s\"." msgstr "Za iskanje id-ja \"%s\" ni zadetkov" msgid "No members." msgstr "Ni Älanov." msgid "None available." msgstr "Noben na voljo." msgid "Not a valid IP protocol number" msgstr "Å tevilka protokola IP ni veljavna." msgid "Not a valid port number" msgstr "Å tevilka vrat ni veljavna" msgid "One colon allowed in port range" msgstr "Ena kolona je dovoljena v razponu vrat" msgid "Other" msgstr "Ostalo" msgid "Password is not accepted" msgstr "Geslo ni bilo sprejeto" msgid "Please log in to continue." msgstr "Za nadaljevanje se prijavite." msgid "Please select a row before taking that action." msgstr "Izberite vrstico preden izberte opravilo." msgid "Processing..." msgstr "Obdelava..." msgid "Save" msgstr "Shrani" #, python-format msgid "Select a %s to browse." msgstr "Izberite %s za iskanje" msgid "Sign In" msgstr "Prijava" msgid "Submit" msgstr "Potrdi" msgid "Success: " msgstr "UspeÅ¡no:" msgid "Summary" msgstr "Povzetek" msgid "The date should be in YYYY-mm-dd format." msgstr "Datum mora biti v obliki YYYY-mm-dd" msgid "This Period's RAM-Hours:" msgstr "RAM-ure za to obdobje" msgid "This Period's VCPU-Hours:" msgstr "VCPU-ure za to obdobje" msgid "This action cannot be undone." msgstr "Te operacije ni mogoÄe preklicati." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Operacije ni mogoÄe izvrÅ¡iti: %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Neavtoriziran dostop. Poskusite se prijaviti in poskusite znova." #, python-format msgid "Unauthorized: %s" msgstr "Niste avtorizirani: %s" msgid "Usage Summary" msgstr "Povzetek porabe" msgid "Warning: " msgstr "Opozorilo:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Za to akcijo nimate ustreznih pravic: %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Nimate pravic za dostop do %s" msgid "You do not have permission to access the resource:" msgstr "Nimate pravic za dostop do teh virov:" horizon-9.0.0/horizon/locale/ja/0000775000567000056710000000000012701407231017667 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/ja/LC_MESSAGES/0000775000567000056710000000000012701407231021454 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/ja/LC_MESSAGES/django.po0000664000567000056710000002376112701407063023272 0ustar jenkinsjenkins00000000000000# Yuko Katabami , 2015. #zanata # Akihiro Motoki , 2016. #zanata # Mie Yamamoto , 2016. #zanata # Yuko Katabami , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-16 02:15+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-15 10:07+0000\n" "Last-Translator: Yuko Katabami \n" "Language-Team: Japanese\n" "Language: ja\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " 別ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã—ã¦ãƒ­ã‚°ã‚¤ãƒ³ã™ã‚‹ã‹ã€" "ホームページ ã«æˆ»ã£ã¦ãã ã•ã„。\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " %(used)s 個使用中 (制é™ãªã—)\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " %(used)s / %(available)s 使用中\n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" "%(start)s " #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" "%(end)s " msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " 使用ã™ã‚‹èªè¨¼ãƒ¡ã‚½ãƒƒãƒ‰ãŒä¸æ˜Žãªå ´åˆã¯ã€ç®¡ç†è€…ã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(data_type)sã®%(action)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(data_type)sã®%(action)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d B" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s ãŒæ­£å¸¸ã«å®Œäº†ã—ã¾ã—ãŸã€‚" #, python-format msgid "%s did not complete." msgstr "%s ãŒå®Œäº†ã—ã¾ã›ã‚“ã§ã—ãŸã€‚" msgid "« Prev" msgstr "« å‰ã¸" msgid "(No Limit)" msgstr "(制é™ãªã—)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 ãƒã‚¤ãƒˆ" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "åå‰ãŒ \"%(name)s\" ã® %(resource)s ãŒã™ã§ã«å­˜åœ¨ã—ã¾ã™ã€‚" msgid "Actions" msgstr "アクション" msgid "Active Instances:" msgstr "稼åƒä¸­ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹:" msgid "Active RAM:" msgstr "使用中ã®ãƒ¡ãƒ¢ãƒªãƒ¼:" msgid "Add a row" msgstr "行ã®è¿½åŠ " msgid "All available" msgstr "利用å¯èƒ½ãªå…¨é …ç›®" msgid "Available" msgstr "利用å¯èƒ½" msgid "Back" msgstr "戻る" #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "Batch Item" #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "Batched Item" msgid "Cancel" msgstr "å–り消ã—" msgid "Connect" msgstr "接続" msgid "Delete" msgstr "削除" msgid "Deleted" msgstr "削除" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "%(content_items)s件表示" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "%(counter)s件表示" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "%(nav_items)s件表示" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "Down Item" #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "Downed Item" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "" "メッセージ㮠json ファイル '%(path)s' ã®å‡¦ç†ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ: " "%(exception)s" msgid "Error: " msgstr "エラー: " msgid "Fake" msgstr "Fake" msgid "Filter" msgstr "フィルター" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "䏿­£ãªå½¢å¼ã® IP アドレス" msgid "Info: " msgstr "情報: " msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "" "メタデータã®ã‚¨ãƒ³ãƒˆãƒªãƒ¼ãŒç„¡åйã§ã™ã€‚コンマ区切り㮠key=value ペアを使用ã—ã¦ãã " "ã•ã„。" msgid "Invalid subnet mask" msgstr "無効ãªã‚µãƒ–ãƒãƒƒãƒˆãƒžã‚¹ã‚¯" msgid "Invalid version for IP address" msgstr "無効㪠IP アドレスã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³" msgid "Limit Summary" msgstr "利用å¯èƒ½ãƒªã‚½ãƒ¼ã‚¹æ¦‚è¦" msgid "Log in" msgstr "ログイン" msgid "Login" msgstr "ログイン" msgid "Members" msgstr "メンãƒãƒ¼" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "" "メッセージ㮠json ファイル '%(path)s' ã®å½¢å¼ãŒæ­£ã—ãã‚りã¾ã›ã‚“。%(exception)s" msgid "More Actions" msgstr "ãã®ä»–ã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³" msgid "Navigation Item" msgstr "ナビゲーション項目" msgid "Never" msgstr "ãªã—" msgid "Next" msgstr "次ã¸" msgid "Next »" msgstr "次㸠»" msgid "No items to display." msgstr "表示ã™ã‚‹é …ç›®ãŒã‚りã¾ã›ã‚“" #, python-format msgid "No match returned for the id \"%s\"." msgstr "ID \"%s\" ã«ä¸€è‡´ã™ã‚‹ã‚‚ã®ãŒã‚りã¾ã›ã‚“。" msgid "No members." msgstr "メンãƒãƒ¼ãŒã„ã¾ã›ã‚“。" msgid "None available." msgstr "利用å¯èƒ½ãªé …ç›®ãŒã‚りã¾ã›ã‚“。" msgid "Not a valid IP protocol number" msgstr "有効㪠IP プロトコル番å·ã§ã¯ã‚りã¾ã›ã‚“" msgid "Not a valid port number" msgstr "有効ãªãƒãƒ¼ãƒˆç•ªå·ã§ã¯ã‚りã¾ã›ã‚“" msgid "One colon allowed in port range" msgstr "ãƒãƒ¼ãƒˆç¯„囲ã§ä½¿ç”¨ã§ãるコロン㯠1 ã¤ã ã‘ã§ã™ã€‚" msgid "Other" msgstr "ãã®ä»–" msgid "Password is not accepted" msgstr "パスワードをå—ã‘付ã‘られã¾ã›ã‚“" msgid "Please log in to continue." msgstr "続行ã™ã‚‹ã«ã¯ã€ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ãã ã•ã„。" msgid "Please select a row before taking that action." msgstr "ã“ã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’実行ã™ã‚‹å‰ã«ã€å¯¾è±¡ã‚’é¸æŠžã—ã¦ãã ã•ã„。" msgid "Processing..." msgstr "処ç†ä¸­..." msgid "Save" msgstr "ä¿å­˜" #, python-format msgid "Select a %s to browse." msgstr "表示ã™ã‚‹ %s ã‚’é¸æŠžã—ã¦ãã ã•ã„。" msgid "Select a period of time to query its usage:" msgstr "使用状æ³ã‚’照会ã™ã‚‹æœŸé–“ã‚’é¸æŠžã—ã¦ãã ã•ã„:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "å­çŠ¬ã‚’å£²ã‚‹" msgid "Sign In" msgstr "ログイン" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "å­çŠ¬ã‚’å£²ã‚Šã¾ã—ãŸ" msgid "Submit" msgstr "é€ä¿¡" msgid "Success: " msgstr "æˆåŠŸ: " msgid "Summary" msgstr "概è¦" #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "%(obj)s ã«å±žæ€§ %(attr)s ãŒå­˜åœ¨ã—ã¾ã›ã‚“。" msgid "The date should be in YYYY-mm-dd format." msgstr "日付㯠YYYY-mm-dd å½¢å¼ã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "The string may only contain ASCII printable characters." msgstr "文字列ã«å«ã‚ã‚‹ã“ã¨ãŒã§ãã‚‹ã®ã¯ ASCII å°å­—å¯èƒ½æ–‡å­—ã®ã¿ã§ã™ã€‚" #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "ã“ã®ãƒ†ãƒ³ãƒ—レート内ã§ã® %(resource)s ã®å€¤ã¯ %(name)s ã§ã™ã€‚ã“ã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェー" "スã‹ã‚‰ã‚¹ã‚¿ãƒƒã‚¯ã‚’èµ·å‹•ã™ã‚‹ã«ã¯ \"http://\" ã¾ãŸã¯ \"https://\" ã§é–‹å§‹ã™ã‚‹å€¤ã‚’" "指定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "This Period's GB-Hours:" msgstr "指定期間中㮠GB 時間:" msgid "This Period's RAM-Hours:" msgstr "指定期間中ã®ãƒ¡ãƒ¢ãƒªãƒ¼æ™‚é–“" msgid "This Period's VCPU-Hours:" msgstr "指定期間中ã®ä»®æƒ³ CPU 時間:" msgid "This action cannot be undone." msgstr "ã“ã®æ“作ã¯å–り消ã›ã¾ã›ã‚“。" #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "%(action)s を実行ã§ãã¾ã›ã‚“: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "èªè¨¼ã•れã¦ã„ã¾ã›ã‚“。もã†ä¸€åº¦ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ãã ã•ã„。" #, python-format msgid "Unauthorized: %s" msgstr "権é™ãŒã‚りã¾ã›ã‚“: %s" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "Up Item" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "é …ç›®ã®æ›´æ–°" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "項目を更新ã—ã¾ã—ãŸ" #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "Upped Item" msgid "Usage Summary" msgstr "使用状æ³ã®æ¦‚è¦" msgid "Warning: " msgstr "警告: " #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "%(action)s ã¯è¨±å¯ã•れã¦ã„ã¾ã›ã‚“: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "%s ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ãŒè¨±å¯ã•れã¦ã„ã¾ã›ã‚“。" msgid "You do not have permission to access the resource:" msgstr "リソースã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹æ¨©é™ãŒã‚りã¾ã›ã‚“:" horizon-9.0.0/horizon/locale/ja/LC_MESSAGES/djangojs.po0000664000567000056710000002265012701407071023622 0ustar jenkinsjenkins00000000000000# Yuko Katabami , 2015. #zanata # Yuko Katabami , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-21 10:33+0000\n" "Last-Translator: Yuko Katabami \n" "Language-Team: Japanese\n" "Language: ja\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s ãƒã‚¤ãƒˆ" msgid "(Modified)" msgstr "(変更済ã¿)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "稼åƒä¸­" msgid "Add Interface" msgstr "インターフェースã®è¿½åŠ " msgid "Added" msgstr "追加済ã¿" msgid "Allocated" msgstr "å‰²ã‚Šå½“ã¦æ¸ˆã¿" msgid "An error occurred while updating." msgstr "更新中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚" msgid "An error occurred. Please try again later." msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚後ã‹ã‚‰ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。" msgid "Available" msgstr "利用å¯èƒ½" msgid "Available Metadata" msgstr "利用å¯èƒ½ãªãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿" msgid "Back" msgstr "戻る" msgid "Cancel" msgstr "å–り消ã—" msgid "Click here for filters." msgstr "フィルターを指定ã™ã‚‹ã«ã¯ã“ã“をクリックã—ã¦ãã ã•ã„。" msgid "Click here to expand the row and view the errors." msgstr "行を展開ã—ã¦ã‚¨ãƒ©ãƒ¼ã‚’表示ã™ã‚‹ã«ã¯ã€ã“ã“をクリックã—ã¦ãã ã•ã„。" msgid "Click to see more details" msgstr "クリックã™ã‚‹ã¨è©³ã—ã„æƒ…å ±ãŒè¡¨ç¤ºã•れã¾ã™ã€‚" msgid "Click to show or hide" msgstr "クリックã—ã¦è¡¨ç¤º/éžè¡¨ç¤ºã‚’切り替ãˆã¦ãã ã•ã„。" msgid "Closed" msgstr "切断済ã¿" msgid "Closing" msgstr "切断中" #, python-format msgid "Confirm %s" msgstr "%sã®ç¢ºèª" msgid "Confirm Delete Foobars" msgstr "Foobar ã®å‰Šé™¤ã®ç¢ºèª" msgid "Connecting" msgstr "接続中" msgid "Could not decrypt the password" msgstr "パスワードã®å¾©å·ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚" msgid "Could not read the file" msgstr "ファイルを読ã¿å–ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸã€‚" msgid "Create Subnet" msgstr "サブãƒãƒƒãƒˆã®ä½œæˆ" msgid "Current Usage" msgstr "ç¾åœ¨ä½¿ç”¨ä¸­" msgid "Custom" msgstr "カスタム" msgid "Customization Script" msgstr "カスタマイズスクリプト" msgid "Danger" msgstr "å±é™º" msgid "Danger: " msgstr "å±é™º:" msgid "Decimal required" msgstr "å進数を指定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "Delete" msgstr "削除" msgid "Delete Instance" msgstr "インスタンスã®å‰Šé™¤" msgid "Delete Interface" msgstr "インタフェースã®å‰Šé™¤" msgid "Delete Network" msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®å‰Šé™¤" msgid "Delete Router" msgstr "ルーターã®å‰Šé™¤" msgid "Delete Subnet" msgstr "サブãƒãƒƒãƒˆã®å‰Šé™¤" #, python-format msgid "Deleted : %s." msgstr "%s を削除ã—ã¾ã—ãŸ" msgid "Detail Information" msgstr "詳細情報" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "%(total)s 件中 %(count)s 件表示" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "%s 件表示" msgid "Duplicate keys are not allowed" msgstr "é‡è¤‡ã™ã‚‹ã‚­ãƒ¼ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“" msgid "Error" msgstr "エラー" msgid "Error: " msgstr "エラー: " msgid "Example" msgstr "例" msgid "Existing Metadata" msgstr "é¸æŠžæ¸ˆã¿ã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿" msgid "Expand to see allocated items" msgstr "å‰²ã‚Šå½“ã¦æ¸ˆã¿ã®ã‚¢ã‚¤ãƒ†ãƒ ã‚’表示ã™ã‚‹ã«ã¯å±•é–‹ã—ã¦ãã ã•ã„。" msgid "Expand to see available items" msgstr "利用å¯èƒ½ãªã‚¢ã‚¤ãƒ†ãƒ ã‚’表示ã™ã‚‹ã«ã¯å±•é–‹ã—ã¦ãã ã•ã„。" msgid "Filter" msgstr "フィルター" msgid "Finish" msgstr "完了" msgid "Flavor" msgstr "フレーãƒãƒ¼" msgid "Full Text Search" msgstr "フルテキスト検索" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "IP アドレス" msgid "Info" msgstr "情報" msgid "Integer required" msgstr "整数を指定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "Interfaces" msgstr "インターフェース" msgid "Load script from a file" msgstr "ファイルã‹ã‚‰ã®ã‚¹ã‚¯ãƒªãƒ—ト読ã¿è¾¼ã¿" msgid "Loading" msgstr "読ã¿è¾¼ã¿ä¸­" msgid "Max" msgstr "最大値" msgid "Max length" msgstr "最大長" msgid "Min" msgstr "最å°å€¤" msgid "Min length" msgstr "最å°é•·" msgid "Name" msgstr "åå‰" msgid "Next" msgstr "次ã¸" msgid "No" msgstr "ã„ã„ãˆ" msgid "No Limit" msgstr "制é™ãªã—" msgid "No available items" msgstr "利用ã§ãるアイテムãŒã‚りã¾ã›ã‚“。" msgid "No available metadata" msgstr "利用å¯èƒ½ãªãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã¯ã‚りã¾ã›ã‚“" msgid "No data available." msgstr "データãŒã‚りã¾ã›ã‚“。" msgid "No description available." msgstr "説明ãŒã‚りã¾ã›ã‚“。" msgid "No existing metadata" msgstr "é¸æŠžæ¸ˆã¿ã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã¯ã‚りã¾ã›ã‚“" msgid "No items to display." msgstr "表示ã™ã‚‹é …ç›®ãŒã‚りã¾ã›ã‚“" msgid "No roles" msgstr "ロールãŒã‚りã¾ã›ã‚“" msgid "None" msgstr "ãªã—" msgid "Not authorized to do this operation." msgstr "ã“ã®æ“ä½œã‚’è¡Œã†æ¨©é™ãŒã‚りã¾ã›ã‚“。" msgid "Notice: " msgstr "注æ„:" msgid "Open" msgstr "オープン" msgid "Open Console" msgstr "コンソールを開ã" msgid "Passwords do not match." msgstr "パスワードãŒä¸€è‡´ã—ã¾ã›ã‚“。" msgid "Pattern mismatch" msgstr "パターンãŒä¸€è‡´ã—ã¾ã›ã‚“。" msgid "Please confirm your selection. " msgstr "é¸æŠžå†…å®¹ã‚’ç¢ºèªã—ã¦ãã ã•ã„。" msgid "Prompt" msgstr "プロンプト" msgid "Re-order items using drag and drop" msgstr "é †ç•ªã‚’ä¸¦ã¹æ›¿ãˆã‚‹ã«ã¯ã€ã‚¢ã‚¤ãƒ†ãƒ ã‚’ドラッグ&ドロップã—ã¦ãã ã•ã„。" msgid "Remaining" msgstr "残り" msgid "Remove" msgstr "削除" msgid "Required" msgstr "å¿…é ˆ" msgid "Roles" msgstr "ロール" msgid "STATUS" msgstr "ステータス" #. Strings between {$ and $} should be left untranslated. msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgstr "" "スクリプトã®ã‚µã‚¤ã‚º: {$ config.MAX_SCRIPT_SIZE | bytes $} 中 {$ (scriptLength " "|| 0) | bytes $}" msgid "Search in current results" msgstr "ç¾åœ¨ã®çµæžœå†…ã®æ¤œç´¢" msgid "Select an item from Available items below" msgstr "以下ã®åˆ©ç”¨å¯èƒ½ãªã‚¢ã‚¤ãƒ†ãƒ ã‹ã‚‰ 1 ã¤é¸æŠžã—ã¦ãã ã•ã„。" msgid "Select one" msgstr "1 ã¤é¸æŠžã—ã¦ãã ã•ã„。" msgid "Server Name" msgstr "サーãƒãƒ¼å" msgid "Shutdown" msgstr "シャットダウン" msgid "Status" msgstr "ステータス" #, python-format msgid "Status: %s" msgstr "ステータス: %s" msgid "Submit" msgstr "é€ä¿¡" msgid "Subnets" msgstr "サブãƒãƒƒãƒˆ" msgid "Success" msgstr "æˆåŠŸ" msgid "Success: " msgstr "æˆåŠŸ: " msgid "Text" msgstr "テキスト" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "æ“作を実行ã§ãã¾ã›ã‚“。ã“ã®è¡Œã®å†…容ã«ã‚¨ãƒ©ãƒ¼ãŒã‚ã‚‹ã‹ã€æƒ…å ±ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。" msgid "The script is larger than the maximum size" msgstr "ã‚¹ã‚¯ãƒªãƒ—ãƒˆãŒæœ€å¤§ã‚µã‚¤ã‚ºã‚’è¶…ãˆã¦ã„ã¾ã™ã€‚" msgid "There was a problem communicating with the server, please try again." msgstr "サーãƒãƒ¼ã¨ã®é€šä¿¡ä¸­ã«å•題ãŒã‚りã¾ã—ãŸã€‚å†åº¦ãŠè©¦ã—ãã ã•ã„。" msgid "There was an error submitting the form. Please try again." msgstr "フォームã®é€ä¿¡ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚å†åº¦ãŠè©¦ã—ãã ã•ã„。" msgid "Toggle Dropdown" msgstr "ドロップダウンã®åˆ‡ã‚Šæ›¿ãˆ" msgid "Toggle navigation" msgstr "ナビゲーションã®åˆ‡ã‚Šæ›¿ãˆ" msgid "Total" msgstr "ç·è¨ˆ" #, python-format msgid "Unable to delete: %s." msgstr "削除ã§ãã¾ã›ã‚“: %s" msgid "Unlimited" msgstr "無制é™" msgid "View Details" msgstr "詳細ã®è¡¨ç¤º" msgid "View Instance Details" msgstr "インスタンスã®è©³ç´°ã®è¡¨ç¤º" msgid "View Router Details" msgstr "ルーターã®è©³ç´°ã®è¡¨ç¤º" msgid "Warning" msgstr "警告" msgid "Warning: " msgstr "警告: " msgid "Working" msgstr "åæ˜ ä¸­" msgid "Yes" msgstr "ã¯ã„" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "å·¦ã®åˆ—ã‹ã‚‰å³ã®åˆ—ã«ã‚¢ã‚¤ãƒ†ãƒ ã‚’移動ã—ã¦ã€ãƒªã‚½ãƒ¼ã‚¹ã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚’指定ã§ãã¾ã™ã€‚å·¦" "ã®åˆ—ã«ã¯ã€Glance ã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚«ã‚¿ãƒ­ã‚°ã«ç™»éŒ²ã•れã¦ã„るメタデータã®å®šç¾©ãŒè¡¨ç¤ºã•" "れã¦ã„ã¾ã™ã€‚ã‚­ãƒ¼ã‚’è‡ªåˆ†ã§æŒ‡å®šã—ã¦ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚’追加ã™ã‚‹ã«ã¯ã€ã€Œã‚«ã‚¹ã‚¿ãƒ ã€ オプ" "ションを使用ã—ã¦ãã ã•ã„。" #, python-format msgid "You have selected %s. " msgstr "%s ã‚’é¸æŠžã—ã¾ã—ãŸã€‚" msgid "description" msgstr "説明" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "プールã®ãƒ‡ãƒ•ォルト値" #, python-format msgid "selected \"%s\"" msgstr "\"%s\" ã‚’é¸æŠžã—ã¾ã—ãŸã€‚" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || '表示ã™ã‚‹é …ç›®ãŒã‚りã¾ã›ã‚“' $}" horizon-9.0.0/horizon/locale/fi_FI/0000775000567000056710000000000012701407231020251 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/fi_FI/LC_MESSAGES/0000775000567000056710000000000012701407231022036 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/fi_FI/LC_MESSAGES/django.po0000664000567000056710000001421712701407063023650 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Finnish (Finland)\n" "Language: fi-FI\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" "%(end)s" #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Tavu" msgstr[1] "%(size)d Tavua" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s tehtiin onnistuneesti." #, python-format msgid "%s did not complete." msgstr "%s ei valmistunut." msgid "« Prev" msgstr "« Edellinen" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 Tavua" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "%(resource)s nimellä \"%(name)s\" on jo olemassa." msgid "Actions" msgstr "Toiminnot" msgid "Active Instances:" msgstr "Aktiiviset Instanssit:" msgid "Active RAM:" msgstr "Aktiivinen RAM:" msgid "Add a row" msgstr "Lisää rivi" msgid "All available" msgstr "Kaikki saatavilla" msgid "Available" msgstr "Saatavilla" msgid "Back" msgstr "Takaisin" msgid "Cancel" msgstr "Keskeytä" msgid "Delete" msgstr "Poista" msgid "Deleted" msgstr "Poistettu" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Näytetään %(content_items)s kohde" msgstr[1] "Näytetään %(content_items)s kohdetta" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Näytetään %(counter)s kohde" msgstr[1] "Näytetään %(counter)s kohdetta" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Näytetään %(nav_items)s kohde" msgstr[1] "Näytetään %(nav_items)s kohdetta" msgid "Error: " msgstr "Virhe:" msgid "Filter" msgstr "Suodatin" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Väärä formaatti IP-osoitteelle" msgid "Info: " msgstr "Info:" msgid "Invalid subnet mask" msgstr "Väärä aliverkon peite" msgid "Invalid version for IP address" msgstr "Väärä versio IP-osoitteesta" msgid "Limit Summary" msgstr "Rajoitusten yhteenveto" msgid "Login" msgstr "Kirjaudu" msgid "Members" msgstr "Jäsenet" msgid "More Actions" msgstr "Lisää Toimintoja" msgid "Navigation Item" msgstr "Navigaatioyksikkö" msgid "Never" msgstr "Ei koskaan" msgid "Next" msgstr "Edellinen" msgid "Next »" msgstr "Seuraava »" msgid "No items to display." msgstr "Ei näytettävää." #, python-format msgid "No match returned for the id \"%s\"." msgstr "ID \"%s\" ei palauttanut osumia." msgid "No members." msgstr "Ei käyttäjiä." msgid "None available." msgstr "Ei mitään saatavilla." msgid "Not a valid IP protocol number" msgstr "IP-protokollan numero on virheellinen" msgid "Not a valid port number" msgstr "Portin numero on virheellinen" msgid "One colon allowed in port range" msgstr "Porttialueessa sallittu vain yksi kaksoispiste" msgid "Other" msgstr "Toinen" msgid "Password is not accepted" msgstr "Salasanaa ei hyväksytty" msgid "Please log in to continue." msgstr "Kirjaudu jatkaaksesi" msgid "Please select a row before taking that action." msgstr "Valitse rivi ennen toiminnon tekemistä." msgid "Processing..." msgstr "Käsitellään..." msgid "Save" msgstr "Tallenna" #, python-format msgid "Select a %s to browse." msgstr "Valitse %s selataksesi." msgid "Select a period of time to query its usage:" msgstr "Valitse näytettävä ajanjakso:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Myy Koiranpentu" msgstr[1] "Myy Koiranpentuja" msgid "Sign In" msgstr "Kirjaudu" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Myyty Koiranpentu" msgstr[1] "Myydyt Koiranpennut" msgid "Submit" msgstr "Lähetä" msgid "Success: " msgstr "Onnistui:" msgid "Summary" msgstr "Yhteenveto" msgid "The date should be in YYYY-mm-dd format." msgstr "Päivämäärän muoto pitää olla VVVV-kk-pv." msgid "This Period's GB-Hours:" msgstr "Tämän ajanjakson GB-tunnit:" msgid "This Period's RAM-Hours:" msgstr "RAM-tunteja tällä aikajaksolla:" msgid "This Period's VCPU-Hours:" msgstr "Tämän ajanjakson VCPU-tunnit:" msgid "This action cannot be undone." msgstr "Tätä toimintoa ei voi peruuttaa." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Ei voida %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Ei oikeutettu: Yritä kirjautumista uudelleen." #, python-format msgid "Unauthorized: %s" msgstr "Ei sallittu: %s" msgid "Usage Summary" msgstr "Käytön yhteenveto" msgid "Warning: " msgstr "Varoitus:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Toiminto %(action)s ei ole sallittu: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Et ole oikeutettu päästäksesi %s" msgid "You do not have permission to access the resource:" msgstr "Sinulla ei ole oikeutta käyttää resurssia:" horizon-9.0.0/horizon/locale/pt_BR/0000775000567000056710000000000012701407231020303 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/pt_BR/LC_MESSAGES/0000775000567000056710000000000012701407231022070 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/pt_BR/LC_MESSAGES/django.po0000664000567000056710000002327712701407071023707 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata # Carlos Marques , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-17 04:04+0000\n" "Last-Translator: Carlos Marques \n" "Language-Team: Portuguese (Brazil)\n" "Language: pt-BR\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " Efetue login como um usuário diferente ou retorne para home page\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " Utilizado %(used)s (No Limit)\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " Usado %(used)s de %(available)s \n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" "Se você não tem certeza do método de autenticação a ser utilizado, entre em " "contato com o seu administrador." #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Byte" msgstr[1] "%(size)d Bytes" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s concluído com sucesso." #, python-format msgid "%s did not complete." msgstr "%s não completou." msgid "« Prev" msgstr "« Prev" msgid "(No Limit)" msgstr "(Sem Limite)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 Bytes" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Um %(resource)s com o nome \"%(name)s\" já existe." msgid "Actions" msgstr "Ações" msgid "Active Instances:" msgstr "Instâncias ativas:" msgid "Active RAM:" msgstr "RAM ativa:" msgid "Add a row" msgstr "Adicionar uma linha" msgid "All available" msgstr "Tudo disponível" msgid "Available" msgstr "Disponível" msgid "Back" msgstr "Voltar" #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "Item em Lote" msgstr[1] "Itens em Lote" #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "Item em Lote" msgstr[1] "Itens em Lote" msgid "Cancel" msgstr "Cancelar" msgid "Connect" msgstr "Conectado" msgid "Delete" msgstr "Excluir" msgid "Deleted" msgstr "Excluído" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Exibindo %(content_items)s item" msgstr[1] "Exibindo %(content_items)s itens" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Exibindo %(counter)s item" msgstr[1] "Exibindo %(counter)s itens" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Exibindo %(nav_items)s item" msgstr[1] "Exibindo %(nav_items)s itens" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "Item Inativo" msgstr[1] "Itens Inativos" #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "Item Desativado" msgstr[1] "Itens Desativados" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "Erro ao processar o arquivo json da mensagem '%(path)s': %(exception)s" msgid "Error: " msgstr "Erro:" msgid "Fake" msgstr "Falso" msgid "Filter" msgstr "Filtro" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Formato incorreto para o endereço IP" msgid "Info: " msgstr "Informação:" msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "" "Metadados inseridos em um formato inválido. Insira os metadados em pares " "Chave=Valor separados por vírgula. " msgid "Invalid subnet mask" msgstr "Máscara de sub-rede inválida" msgid "Invalid version for IP address" msgstr "Versão inválida para o endereço IP" msgid "Limit Summary" msgstr "Resumo de Limites" msgid "Log in" msgstr "Efetuar Log in" msgid "Login" msgstr "Logar" msgid "Members" msgstr "Membros" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "O arquivo json da mensagem '%(path)s' está malformado. %(exception)s" msgid "More Actions" msgstr "Mais Ações" msgid "Navigation Item" msgstr "Item de Navegação" msgid "Never" msgstr "Nunca" msgid "Next" msgstr "Próximo" msgid "Next »" msgstr "Next »" msgid "No items to display." msgstr "Sem itens para exibir." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Não foi encontrada correspondência para o ID \"%s\"." msgid "No members." msgstr "Sem membros." msgid "None available." msgstr "Nenhum disponível." msgid "Not a valid IP protocol number" msgstr "Não é um número de protocolo IP válido" msgid "Not a valid port number" msgstr "Não é um número de porta válido" msgid "One colon allowed in port range" msgstr "Uma pontução de dois pontos permitida no intervalo de portas" msgid "Other" msgstr "Outros" msgid "Password is not accepted" msgstr "Senha não foi aceita" msgid "Please log in to continue." msgstr "Por favor faça login para continuar." msgid "Please select a row before taking that action." msgstr "Por favor selecione uma linha antes de realizar esta ação." msgid "Processing..." msgstr "Processando..." msgid "Save" msgstr "Salvar" #, python-format msgid "Select a %s to browse." msgstr "Selecione um %s para navegar." msgid "Select a period of time to query its usage:" msgstr "Selecione um período de tempo para consultar seu uso:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Vender Puppy" msgstr[1] "Vender Puppies" msgid "Sign In" msgstr "Entrar" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Puppy Vendido" msgstr[1] "Puppies Vendidos" msgid "Submit" msgstr "Enviar" msgid "Success: " msgstr "Sucesso:" msgid "Summary" msgstr "Resumo" #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "O atributo %(attr)s não existe em %(obj)s." msgid "The date should be in YYYY-mm-dd format." msgstr "A data deve estar no formato YYYY-mm-dd." msgid "The string may only contain ASCII printable characters." msgstr "A string somente deve conter caracteres ASCII imprimíveis." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "O valor de %(resource)s é %(name)s dentro do modelo. Ao ativar uma pilha a " "partir dessa interface, o valor deverá iniciar com \"http://\" ou \"https://" "\"" msgid "This Period's GB-Hours:" msgstr "GB-Horas desse Período:" msgid "This Period's RAM-Hours:" msgstr "Quantidade de RAM-Horas deste período:" msgid "This Period's VCPU-Hours:" msgstr "VCPU-Horas desse Período:" msgid "This action cannot be undone." msgstr "Esta ação não pode ser desfeita." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Não foi possível %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Não autorizado. Por favor tente efetuar login novamente." #, python-format msgid "Unauthorized: %s" msgstr "Não autorizado: %s" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "Item Ativo" msgstr[1] "Itens Ativos" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "Atualizar Item" msgstr[1] "Atualizar Itens" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "Item Atualizado" msgstr[1] "Itens Atualizados" #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "Item Ativado" msgstr[1] "Itens Ativados" msgid "Usage Summary" msgstr "Resumo de Utilização" msgid "Warning: " msgstr "Alerta:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Você não possui permissão para %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Você não está autorizado a acessar %s" msgid "You do not have permission to access the resource:" msgstr "Você não tem permissão para acessar o recurso:" horizon-9.0.0/horizon/locale/pt_BR/LC_MESSAGES/djangojs.po0000664000567000056710000002073412701407063024240 0ustar jenkinsjenkins00000000000000# André Campos , 2015. #zanata # Gabriel Wainer , 2015. #zanata # OpenStack Infra , 2015. #zanata # Carlos Marques , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-07 10:13+0000\n" "Last-Translator: Carlos Marques \n" "Language-Team: Portuguese (Brazil)\n" "Language: pt-BR\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s bytes" msgid "(Modified)" msgstr "(Modificado)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "Ativo" msgid "Add Interface" msgstr "Adicionar Interface" msgid "Added" msgstr "Adicionado" msgid "Allocated" msgstr "Alocado" msgid "An error occurred while updating." msgstr "Um erro ocorreu ao atualizar." msgid "An error occurred. Please try again later." msgstr "Um erro ocorreu. Por favor tente novamente mais tarde." msgid "Available" msgstr "Disponível" msgid "Available Metadata" msgstr "Metadados disponível" msgid "Back" msgstr "Voltar" msgid "Cancel" msgstr "Cancelar" msgid "Click here for filters." msgstr "Clique aqui para filtros." msgid "Click here to expand the row and view the errors." msgstr "Clique aqui para expandir a linha e ver os erros." msgid "Click to see more details" msgstr "Clique para ver mais detalhes." msgid "Click to show or hide" msgstr "Clique para exibir ou esconder" msgid "Closed" msgstr "Encerrado" msgid "Closing" msgstr "Encerramento" #, python-format msgid "Confirm %s" msgstr "Confirma %s" msgid "Confirm Delete Foobars" msgstr "Confirmar Exclusão de Foobars " msgid "Connecting" msgstr "Conectando" msgid "Could not decrypt the password" msgstr "Não foi possível descriptografar a senha" msgid "Could not read the file" msgstr "Não foi possivel ler o arquivo" msgid "Create Subnet" msgstr "Criar Sub-rede" msgid "Current Usage" msgstr "Uso atual" msgid "Custom" msgstr "Customizado" msgid "Customization Script" msgstr "Script de customização" msgid "Danger" msgstr "Perigo" msgid "Danger: " msgstr "Perigo:" msgid "Decimal required" msgstr "Decimal requerido" msgid "Delete" msgstr "Excluir" msgid "Delete Instance" msgstr "Excluir Instância" msgid "Delete Interface" msgstr "Excluir Interface" msgid "Delete Network" msgstr "Excluir Rede" msgid "Delete Router" msgstr "Excluir Roteador" msgid "Delete Subnet" msgstr "Excluir Sub-rede" #, python-format msgid "Deleted : %s." msgstr "Excluídos : %s." msgid "Detail Information" msgstr "Informações delhadas" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "Exibindo %(count)s de %(total)s itens" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "Exibindo %s item" msgstr[1] "Exibindo %s itens" msgid "Duplicate keys are not allowed" msgstr "Chaves duplicadas não são permitidas" msgid "Error" msgstr "Erro" msgid "Error: " msgstr "Erro:" msgid "Example" msgstr "Exemplo" msgid "Existing Metadata" msgstr "Metadados existente" msgid "Expand to see allocated items" msgstr "Expanda para ver os itens alocados" msgid "Expand to see available items" msgstr "Expanda para ver os itens disponíveis" msgid "Filter" msgstr "Filtro" msgid "Finish" msgstr "Encerrar" msgid "Flavor" msgstr "Flavor" msgid "Full Text Search" msgstr "Procura de Texto Completa" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "Endereço IP" msgid "Info" msgstr "Info" msgid "Integer required" msgstr "Inteiro requerido" msgid "Interfaces" msgstr "Interfaces" msgid "Load script from a file" msgstr "Carregar script a partir de um arquivo" msgid "Loading" msgstr "Carregando" msgid "Max" msgstr "Máx." msgid "Max length" msgstr "Tamanho máximo" msgid "Min" msgstr "Mín. " msgid "Min length" msgstr "Tamanho mínimo" msgid "Name" msgstr "Nome" msgid "Next" msgstr "Próximo" msgid "No" msgstr "Não" msgid "No Limit" msgstr "Nenhum limite" msgid "No available items" msgstr "Itens indisponíveis" msgid "No available metadata" msgstr "Metadados não disponível" msgid "No data available." msgstr "Não há dados disponíveis." msgid "No description available." msgstr "Sem descrição disponível." msgid "No existing metadata" msgstr "Metadados não existente" msgid "No items to display." msgstr "Sem itens para exibir." msgid "No roles" msgstr "Sem papéis" msgid "None" msgstr "Nenhum" msgid "Not authorized to do this operation." msgstr "Não autorizado para realizar esta operação." msgid "Notice: " msgstr "Notificação:" msgid "Open" msgstr "Abrir" msgid "Open Console" msgstr "Abrir Console" msgid "Passwords do not match." msgstr "As senhas não conferem." msgid "Pattern mismatch" msgstr "Incompatibilidade de padrão" msgid "Please confirm your selection. " msgstr "Por favor confirme a sua seleção." msgid "Prompt" msgstr "Prompt" msgid "Re-order items using drag and drop" msgstr "reordene os itens clicando em um deles e arrastando-o" msgid "Remaining" msgstr "Restante" msgid "Remove" msgstr "Remover" msgid "Required" msgstr "Requerido" msgid "Roles" msgstr "Papéis" msgid "STATUS" msgstr "STATUS" #. Strings between {$ and $} should be left untranslated. msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgstr "" "Tamanho do script: {$ (scriptLength || 0) | bytes $} de {$ config." "MAX_SCRIPT_SIZE | bytes $}" msgid "Search in current results" msgstr "Procurar nos resultados atuais" msgid "Select an item from Available items below" msgstr "Selecione um item dentre os disponíveis abaixo" msgid "Select one" msgstr "Selecione" msgid "Server Name" msgstr "Nome do Servidor" msgid "Shutdown" msgstr "Desligar" msgid "Status" msgstr "Status" #, python-format msgid "Status: %s" msgstr "Status: %s" msgid "Submit" msgstr "Enviar" msgid "Subnets" msgstr "Sub-redes" msgid "Success" msgstr "Sucesso" msgid "Success: " msgstr "Sucesso:" msgid "Text" msgstr "Texto" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "A ação não pôde ser executada. O conteúdo desta linha possui erros ou " "informações indisponíveis" msgid "The script is larger than the maximum size" msgstr "O script é maior que o tamanho máximo" msgid "There was a problem communicating with the server, please try again." msgstr "" "Houve um problema ao comunicar-se com o servidor, por favor tente novamente." msgid "There was an error submitting the form. Please try again." msgstr "Houve um erro ao enviar o formulário. Por favor tente novamente." msgid "Toggle Dropdown" msgstr "Alternar Opções" msgid "Toggle navigation" msgstr "Alternar navegação" msgid "Total" msgstr "Total" #, python-format msgid "Unable to delete: %s." msgstr "Não é possível excluir: %s." msgid "Unlimited" msgstr "Ilimitado" msgid "View Details" msgstr "Ver Detalhes" msgid "View Instance Details" msgstr "Ver Detalhes da Instância" msgid "View Router Details" msgstr "Ver Detalhes do Roteador" msgid "Warning" msgstr "Aviso" msgid "Warning: " msgstr "Alerta:" msgid "Working" msgstr "Trabalhando" msgid "Yes" msgstr "Sim" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "É possível especificar metadados de recurso ao mover os itens da coluna " "esquerda para a coluna direita. Na coluna esquerda, há definições de " "metadados a partir do Catálogo de Metadados Glance. Use a opção \"Customizado" "\" para incluir metadados com a chave de sua opção." #, python-format msgid "You have selected %s. " msgstr "Você selecionou %s." msgid "description" msgstr "descrição" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "pool default" #, python-format msgid "selected \"%s\"" msgstr "\"%s\" selecionados" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || 'No items to display.' $}" horizon-9.0.0/horizon/locale/it/0000775000567000056710000000000012701407231017711 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/it/LC_MESSAGES/0000775000567000056710000000000012701407231021476 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/it/LC_MESSAGES/django.po0000664000567000056710000002374712701407071023317 0ustar jenkinsjenkins00000000000000# Alessandra , 2015. #zanata # Mattia Gandolfi , 2015. #zanata # OpenStack Infra , 2015. #zanata # Alessandra , 2016. #zanata # Remo Mattei , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-28 03:42+0000\n" "Last-Translator: Alessandra \n" "Language-Team: Italian\n" "Language: it\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " Login con un utente diverso oppure ritorna indietro a home\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " In uso %(used)s (No Limit)\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " In uso %(used)s of %(available)s \n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" "In caso di dubbi sul metodo di autenticazione da utilizzare, contattare " "l'amministratore.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d byte" msgstr[1] "%(size)d byte" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s completato correttamente." #, python-format msgid "%s did not complete." msgstr "%s non completato." msgid "« Prev" msgstr "« Precedente" msgid "(No Limit)" msgstr "(Nessun limite)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 byte" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "La risorsa %(resource)s con il nome \"%(name)s\" esiste." msgid "Actions" msgstr "Azioni" msgid "Active Instances:" msgstr "Istanze attive:" msgid "Active RAM:" msgstr "RAM attiva:" msgid "Add a row" msgstr "Aggiungi una linea" msgid "All available" msgstr "Tutti disponibili" msgid "Available" msgstr "Disponibile" msgid "Back" msgstr "Indietro" #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "Elemento raggruppato" msgstr[1] "Elementi raggruppati " #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "Elemento è stato raggruppato " msgstr[1] "Elementi sono stati raggruppati " msgid "Cancel" msgstr "Annulla" msgid "Connect" msgstr "Collegati" msgid "Delete" msgstr "Elimina" msgid "Deleted" msgstr "Eliminato" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Visualizzazione dell'elemento %(content_items)s" msgstr[1] "Visualizzazione degli elementi %(content_items)s" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Visualizzazione dell'elemento %(counter)s" msgstr[1] "Visualizzazione degli elementi %(counter)s" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Visualizzazione dell'elemento %(nav_items)s" msgstr[1] "Visualizzazione degli elementi %(nav_items)s" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "Elemento sottostante" msgstr[1] "Elementi sottostanti " #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "Elemento sotto" msgstr[1] "Elementi sotto" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "Errore nel processare il messaggio json file '%(path)s': %(exception)s" msgid "Error: " msgstr "Errore:" msgid "Fake" msgstr "Falso" msgid "Filter" msgstr "Filtro" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Formato incorretto per l'indirizzo IP" msgid "Info: " msgstr "Info: " msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "" "Voce di metadati non valida. Utilizzare le coppie key=value separate da " "virgola" msgid "Invalid subnet mask" msgstr "Maschera sottorete non valida." msgid "Invalid version for IP address" msgstr "Versione dell'indirizzo IP non valida" msgid "Limit Summary" msgstr "Riepilogo dei Limiti" msgid "Log in" msgstr "Log in" msgid "Login" msgstr "Login" msgid "Members" msgstr "Membri" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "Il messaggio del file json '%(path)s' è malformato. %(exception)s" msgid "More Actions" msgstr "Altre azioni" msgid "Navigation Item" msgstr "Elemento di navigazione" msgid "Never" msgstr "Mai" msgid "Next" msgstr "Prossimo" msgid "Next »" msgstr "Successivo »" msgid "No items to display." msgstr "Nessun elemento da visualizzare" #, python-format msgid "No match returned for the id \"%s\"." msgstr "Nessuna corrispondenza restituita per l'identificativo \"%s\"" msgid "No members." msgstr "Nessun membro." msgid "None available." msgstr "Nessuno disponibile." msgid "Not a valid IP protocol number" msgstr "Numero protocollo IP non valido" msgid "Not a valid port number" msgstr "Numero porta non valido" msgid "One colon allowed in port range" msgstr "Una virgola permessa nell'intervallo delle porte" msgid "Other" msgstr "Altro" msgid "Password is not accepted" msgstr "La password non è stata accettata." msgid "Please log in to continue." msgstr "Accedi per continuare" msgid "Please select a row before taking that action." msgstr "Per cortesia, selezionare una riga prima di eseguire tale azione." msgid "Processing..." msgstr "Attendere prego..." msgid "Save" msgstr "Salva" #, python-format msgid "Select a %s to browse." msgstr "Selezionare un %s per sfogliare." msgid "Select a period of time to query its usage:" msgstr "" "Selezionare un periodo di tempo per eseguire la query sul relativo utilizzo:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Vendi cucciolo" msgstr[1] "Vendi cuccioli" msgid "Sign In" msgstr "Accesso" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Cucciolo venduto" msgstr[1] "Cuccioli venduti" msgid "Submit" msgstr "Invia" msgid "Success: " msgstr "Successo:" msgid "Summary" msgstr "Riepilogo" #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "Questo attributo %(attr)s non esiste su questo %(obj)s. " msgid "The date should be in YYYY-mm-dd format." msgstr "La data deve essere nel formato YYYY-mm-dd." msgid "The string may only contain ASCII printable characters." msgstr "La stringa può contenere solo caratteri stampabili ASCII." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "Il valore di %(resource)s è %(name)s all'interno del template. Quando si " "avvia uno stack da questa interfaccia, il valore deve iniziare con \"http://" "\" o \"https://\"" msgid "This Period's GB-Hours:" msgstr "Ore-GB di questo periodo:" msgid "This Period's RAM-Hours:" msgstr "Ore-RAM di questo periodo:" msgid "This Period's VCPU-Hours:" msgstr "Ore-VCPU di questo periodo:" msgid "This action cannot be undone." msgstr "Questa azione non può essere annullata" #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Impossibile %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Non autorizzato. Ritentare il login." #, python-format msgid "Unauthorized: %s" msgstr "Non autorizzato: %s" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "Elemento soprastante " msgstr[1] "Elementi soprastanti " msgid "Update Item" msgid_plural "Update Items" msgstr[0] "Aggiorna l'elemento" msgstr[1] "Aggiorna gl' elementi " msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "Elemento Aggiornato" msgstr[1] "Elementi Aggiornati " #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "Elemento sopra" msgstr[1] "Elementi sopra " msgid "Usage Summary" msgstr "Riepilogo dell'Utilizzo" msgid "Warning: " msgstr "Attenzione:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Non si è autorizzati a %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Accesso non autorizzato a %s" msgid "You do not have permission to access the resource:" msgstr "Non si dispone dei permessi per accedere alla risorsa:" horizon-9.0.0/horizon/locale/it/LC_MESSAGES/djangojs.po0000664000567000056710000002124712701407071023645 0ustar jenkinsjenkins00000000000000# Alessandra , 2015. #zanata # Mattia Gandolfi , 2015. #zanata # OpenStack Infra , 2015. #zanata # Alessandra , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-28 03:49+0000\n" "Last-Translator: Alessandra \n" "Language-Team: Italian\n" "Language: it\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s bytes" msgid "(Modified)" msgstr "(Modificato)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "Attivo" msgid "Add Interface" msgstr "Aggiungi interfaccia" msgid "Added" msgstr "Aggiunto" msgid "Allocated" msgstr "Allocato" msgid "An error occurred while updating." msgstr "Si è verificato un errore durante l'aggiornamento." msgid "An error occurred. Please try again later." msgstr "Si è verificato un errore. Per favore riprova più tardi." msgid "Available" msgstr "Disponibile" msgid "Available Metadata" msgstr "Metadati disponibili" msgid "Back" msgstr "Indietro" msgid "Cancel" msgstr "Annulla" msgid "Click here for filters." msgstr "Fare clic qui per i filtri." msgid "Click here to expand the row and view the errors." msgstr "Fare clic qui per espandere la riga e visualizzare gli errori." msgid "Click to see more details" msgstr "Fare clic per visualizzare ulteriori dettagli" msgid "Click to show or hide" msgstr "Fare clic per visualizzare o nascondere" msgid "Closed" msgstr "Chiuso" msgid "Closing" msgstr "Chiusura" #, python-format msgid "Confirm %s" msgstr "Conferma %s" msgid "Confirm Delete Foobars" msgstr "Conferma eliminazione foobar" msgid "Connecting" msgstr "Connessione" msgid "Could not decrypt the password" msgstr "Impossibile decodificare la password" msgid "Could not read the file" msgstr "Impossibile leggere il file" msgid "Create Subnet" msgstr "Crea sottorete" msgid "Current Usage" msgstr "Utilizzo attuale" msgid "Custom" msgstr "Personalizzato" msgid "Customization Script" msgstr "Script di personalizzazione" msgid "Danger" msgstr "Pericolo" msgid "Danger: " msgstr "Pericolo:" msgid "Decimal required" msgstr "Numero decimale richiesto" msgid "Delete" msgstr "Elimina" msgid "Delete Instance" msgstr "Elimina istanza" msgid "Delete Interface" msgstr "Elimina interfaccia" msgid "Delete Network" msgstr "Elimina rete" msgid "Delete Router" msgstr "Elimina router" msgid "Delete Subnet" msgstr "Elimina sottorete" #, python-format msgid "Deleted : %s." msgstr "Eliminato : %s." msgid "Detail Information" msgstr "Informazioni dettagliate" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "Visualizzazione di %(count)s di %(total)s elementi" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "Mostrando %s oggetti" msgstr[1] "Mostrando %s oggetti" msgid "Duplicate keys are not allowed" msgstr "Le chiavi duplicate non sono consentite" msgid "Error" msgstr "Errore" msgid "Error: " msgstr "Errore:" msgid "Example" msgstr "Esempio" msgid "Existing Metadata" msgstr "Metadati esistenti" msgid "Expand to see allocated items" msgstr "Espandi per visualizzare gli elementi allocati" msgid "Expand to see available items" msgstr "Espandi per visualizzare gli elementi disponibili" msgid "Filter" msgstr "Filtro" msgid "Finish" msgstr "Fine" msgid "Flavor" msgstr "Sapore" msgid "Full Text Search" msgstr "Ricerca testo completo" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "Indirizzi IP" msgid "Info" msgstr "Informazioni" msgid "Integer required" msgstr "Numero intero richiesto" msgid "Interfaces" msgstr "Interfacce" msgid "Load script from a file" msgstr "Carica script da un file" msgid "Loading" msgstr "Caricamento" msgid "Max" msgstr "Massimo" msgid "Max length" msgstr "Lunghezza massima" msgid "Min" msgstr "Min" msgid "Min length" msgstr "Lunghezza minima" msgid "Name" msgstr "Nome" msgid "Next" msgstr "Prossimo" msgid "No" msgstr "No" msgid "No Limit" msgstr "Nessun limite" msgid "No available items" msgstr "Nessun elemento disponibile" msgid "No available metadata" msgstr "Nessun metadato disponibile" msgid "No data available." msgstr "Nessun dato disponibile." msgid "No description available." msgstr "Nessuna descrizione disponibile." msgid "No existing metadata" msgstr "Nessun metadato esistente" msgid "No items to display." msgstr "Nessun elemento da visualizzare" msgid "No roles" msgstr "Nessun ruolo" msgid "None" msgstr "None" msgid "Not authorized to do this operation." msgstr "Non si è autorizzati a eseguire questa operazione." msgid "Notice: " msgstr "Avviso:" msgid "Open" msgstr "Apri" msgid "Open Console" msgstr "Apri console" msgid "Passwords do not match." msgstr "La password non coincide." msgid "Pattern mismatch" msgstr "Mancata corrispondenza del modello" msgid "Please confirm your selection. " msgstr "Confermare la selezione." msgid "Prompt" msgstr "Prompt" msgid "Re-order items using drag and drop" msgstr "Riordina gli elementi utilizzando la funzione di trascinamento" msgid "Remaining" msgstr "Restante" msgid "Remove" msgstr "Rimuovere" msgid "Required" msgstr "Richiesto" msgid "Roles" msgstr "Ruoli" msgid "STATUS" msgstr "STATO" #. Strings between {$ and $} should be left untranslated. msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgstr "" "Dimensione script: {$ (scriptLength || 0) | bytes $} of {$ config." "MAX_SCRIPT_SIZE | bytes $}" msgid "Search in current results" msgstr "Cerca nei risultati correnti" msgid "Select an item from Available items below" msgstr "Selezionare un elemento dagli elementi disponibili in basso" msgid "Select one" msgstr "Seleziona uno" msgid "Server Name" msgstr "Nome server" msgid "Shutdown" msgstr "Chiusura" msgid "Status" msgstr "Stato" #, python-format msgid "Status: %s" msgstr "Stato: %s" msgid "Submit" msgstr "Invia" msgid "Subnets" msgstr "Sottoreti" msgid "Success" msgstr "Riuscito" msgid "Success: " msgstr "Successo:" msgid "Text" msgstr "Testo" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "L'azione non può essere eseguita. Il contenuto di questa riga contiene o " "errori o informazioni mancanti" msgid "The script is larger than the maximum size" msgstr "Lo script è maggiore della dimensione massima" msgid "There was a problem communicating with the server, please try again." msgstr "Si è verificato un errore di comunicazione con il server, riprova." msgid "There was an error submitting the form. Please try again." msgstr "Si è verificato un errore durante l'invio dei dati. Riprova." msgid "Toggle Dropdown" msgstr "Attiva/disattiva elenco a discesa" msgid "Toggle navigation" msgstr "Attiva/disattiva navigazione" msgid "Total" msgstr "Totale" #, python-format msgid "Unable to delete: %s." msgstr "Impossibile eliminare: %s." msgid "Unlimited" msgstr "Illimitato" msgid "View Details" msgstr "Visualizza dettagli" msgid "View Instance Details" msgstr "Visualizza dettagli istanza" msgid "View Router Details" msgstr "Visualizza dettagli router" msgid "Warning" msgstr "Avvertenza" msgid "Warning: " msgstr "Attenzione:" msgid "Working" msgstr "In corso" msgid "Yes" msgstr "Si" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "È possibile specificare i metadati della risorsa spostando gli elementi " "dalla colonna di sinistra alla colonna di destra. Nella colonna di sinistra " "sono presenti definizioni di metadati del catalogo di metadati glance. " "Utilizzare l'opzione \"Personalizzato\" per aggiungere i metadati con la " "chiave desiderata." #, python-format msgid "You have selected %s. " msgstr "Hai selezionato %s." msgid "description" msgstr "descrizione" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "pool predefinito" #, python-format msgid "selected \"%s\"" msgstr "selezionato \"%s\"" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || 'Nessun elemento da visualizzare.' $}" horizon-9.0.0/horizon/locale/es_MX/0000775000567000056710000000000012701407231020310 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/es_MX/LC_MESSAGES/0000775000567000056710000000000012701407231022075 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/es_MX/LC_MESSAGES/django.po0000664000567000056710000001435212701407063023707 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Spanish (Mexico)\n" "Language: es-MX\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" "%(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " Si no está seguro cual método de verificación de usuario a usar, " "contacte a su administrador.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Byte" msgstr[1] "%(size)d Bytes" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s completado correctamente." #, python-format msgid "%s did not complete." msgstr "%s no completado." msgid "« Prev" msgstr "« Anterior" msgid "(No Limit)" msgstr "(Sin Límite)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 Bytes" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Un %(resource)s con el nombre \"%(name)s\" ya existe." msgid "Actions" msgstr "Acciones" msgid "Active Instances:" msgstr "Instancias Activas:" msgid "Active RAM:" msgstr "RAM Activa:" msgid "Add a row" msgstr "Agregar un renglón" msgid "All available" msgstr "Todos los disponibles" msgid "Available" msgstr "Disponible" msgid "Back" msgstr "Regresar" msgid "Cancel" msgstr "Cancelar" msgid "Connect" msgstr "Conectar" msgid "Delete" msgstr "Borrar" msgid "Deleted" msgstr "Borrado" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Mostrando %(content_items)s elemento" msgstr[1] "Mostrando %(content_items)s elementos" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Mostrando %(counter)s elemento" msgstr[1] "Mostrando %(counter)s elementos" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Mostrando %(nav_items)s elemento" msgstr[1] "Mostrando %(nav_items)s elementos" msgid "Error: " msgstr "Error: " msgid "Filter" msgstr "Filtro" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Formato de dirección IP incorrecto" msgid "Info: " msgstr "Info:" msgid "Invalid subnet mask" msgstr "Máscara de red no válida" msgid "Invalid version for IP address" msgstr "Versión de dirección IP no válida" msgid "Limit Summary" msgstr "Resumen de Límite" msgid "Login" msgstr "Nombre de usuario" msgid "Members" msgstr "Miembros" msgid "More Actions" msgstr "Más Acciones" msgid "Navigation Item" msgstr "Ãtem de navegación" msgid "Never" msgstr "Nunca" msgid "Next" msgstr "Siguiente" msgid "Next »" msgstr "Siguiente »" msgid "No items to display." msgstr "No hay ítems que mostrar." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Ninguna coincidencia para el id \"%s\"." msgid "No members." msgstr "Sin miembros." msgid "None available." msgstr "Ninguno disponible." msgid "Not a valid IP protocol number" msgstr "No es un número de protocolo IP válido" msgid "Not a valid port number" msgstr "No es un número de puerto valido" msgid "One colon allowed in port range" msgstr ": se permite en el rango de puertos" msgid "Other" msgstr "Otro" msgid "Password is not accepted" msgstr "La contraseña no se ha aceptado" msgid "Please log in to continue." msgstr "Inicie sesión para continuar." msgid "Please select a row before taking that action." msgstr "Seleccione una fila antes de realizar la acción." msgid "Processing..." msgstr "Procesando..." msgid "Save" msgstr "Guardar" #, python-format msgid "Select a %s to browse." msgstr "Seleccione una %s para navegar." msgid "Select a period of time to query its usage:" msgstr "Seleccione un periodo de tiempo para consultar su uso:" msgid "Sign In" msgstr "Darse de alta" msgid "Submit" msgstr "Enviar" msgid "Success: " msgstr "Correcto:" msgid "Summary" msgstr "Resumen" msgid "The date should be in YYYY-mm-dd format." msgstr "La fecha debe estar en formato YYYY-mm-dd." msgid "The string may only contain ASCII printable characters." msgstr "El string puede unicamente contener caracteres ASCII imprimibles." msgid "This Period's GB-Hours:" msgstr "GB-Horas de este Periodo:" msgid "This Period's RAM-Hours:" msgstr "RAM-Horas de este Periodo:" msgid "This action cannot be undone." msgstr "Esta acción no puede ser deshecha." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "No ha sido posible %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "No autorizado. Inicie sesión de nuevo." #, python-format msgid "Unauthorized: %s" msgstr "No autorizado: %s" msgid "Usage Summary" msgstr "Resumen de Uso" msgid "Warning: " msgstr "Advertencia:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "No esta permitido a %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "No está autorizado para acceder a %s" msgid "You do not have permission to access the resource:" msgstr "No tiene permiso para acceder el recurso:" horizon-9.0.0/horizon/locale/de/0000775000567000056710000000000012701407231017665 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/de/LC_MESSAGES/0000775000567000056710000000000012701407231021452 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/de/LC_MESSAGES/django.po0000664000567000056710000002363512701407063023270 0ustar jenkinsjenkins00000000000000# Carsten Duch , 2015. #zanata # Carsten Duch , 2016. #zanata # Lisa Stemmler , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-16 02:15+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-15 11:37+0000\n" "Last-Translator: Lisa Stemmler \n" "Language-Team: German\n" "Language: de\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " Login as different user or go back to home page\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " Verwendet %(used)s (keine Begrenzung)\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " Verwendet %(used)s von %(available)s \n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " Wenn Sie nicht sicher sind, welche Authentifizierungsmethode zu " "verwenden ist, kontaktieren Sie Ihren Administrator.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Byte" msgstr[1] "%(size)d Bytes" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s erfolgreich abgeschlossen." #, python-format msgid "%s did not complete." msgstr "%s nicht abgeschlossen." msgid "« Prev" msgstr "« Zurück" msgid "(No Limit)" msgstr "(Keine Begrenzung)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 Bytes" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Ein(e) %(resource)s mit dem Namen \"%(name)s\" existiert bereits." msgid "Actions" msgstr "Aktionen" msgid "Active Instances:" msgstr "Aktive Instanzen:" msgid "Active RAM:" msgstr "Aktiver RAM:" msgid "Add a row" msgstr "Eine Zeile hinzufügen" msgid "All available" msgstr "Alle verfügbaren" msgid "Available" msgstr "Verfügbar" msgid "Back" msgstr "Zurück" #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "Eintrag Stapeln" msgstr[1] "Einträge Stapeln" #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "Stapel Eintrag" msgstr[1] "Stapel Einträge" msgid "Cancel" msgstr "Abbrechen" msgid "Connect" msgstr "Verbinden" msgid "Delete" msgstr "Löschen" msgid "Deleted" msgstr "Gelöscht" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Zeige %(content_items)s Eintrag" msgstr[1] "Zeige %(content_items)s Einträge" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Zeige %(counter)s Eintrag" msgstr[1] "Zeige %(counter)s Einträge" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Zeige %(nav_items)s Eintrag" msgstr[1] "Zeige %(nav_items)s Einträge" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "Eintrag Heruntersetzen" msgstr[1] "Einträge Heruntersetzen" #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "Heruntergesetzter Eintrag" msgstr[1] "Heruntergesetzte Einträge" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "Fehler beim Verarbeiten der json-Datei '%(path)s': %(exception)s" msgid "Error: " msgstr "Fehler:" msgid "Fake" msgstr "Fälschung" msgid "Filter" msgstr "Filter" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Ungültiges Format der IP-Adresse" msgid "Info: " msgstr "Information:" msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "" "Ungültiger Metadateneintrag. Verwenden Sie durch Kommas separierte " "Schlüssel=Wert Paare" msgid "Invalid subnet mask" msgstr "Ungültige Subnetzmaske" msgid "Invalid version for IP address" msgstr "Ungültige Version der IP-Adresse" msgid "Limit Summary" msgstr "Übersicht Begrenzungen" msgid "Log in" msgstr "Anmeldung" msgid "Login" msgstr "Login" msgid "Members" msgstr "Mitglieder" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "" "Nachrichten JSON Datei '%(path)s' ist im falschen Format. %(exception)s" msgid "More Actions" msgstr "Weitere Aktionen" msgid "Navigation Item" msgstr "Navigations-Eintrag" msgid "Never" msgstr "Niemals" msgid "Next" msgstr "Weiter" msgid "Next »" msgstr "Weiter »" msgid "No items to display." msgstr "Keine Einträge zum Anzeigen." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Keine Übereinstimmung für die ID \"%s\"." msgid "No members." msgstr "Keine Mitglieder." msgid "None available." msgstr "Keine verfügbar." msgid "Not a valid IP protocol number" msgstr "Keine gültige IP-Protokollnummer" msgid "Not a valid port number" msgstr "Keine gültige Port-Nummer" msgid "One colon allowed in port range" msgstr "Im Port-Bereich ist nur ein Doppelpunkt erlaubt" msgid "Other" msgstr "Andere" msgid "Password is not accepted" msgstr "Password wurde nicht akzeptiert" msgid "Please log in to continue." msgstr "Bitte melden Sie sich an um fortzufahren." msgid "Please select a row before taking that action." msgstr "Bitte wählen Sie vor dem Ausführen dieser Aktion eine Zeile aus." msgid "Processing..." msgstr "Verarbeite..." msgid "Save" msgstr "Speichern" #, python-format msgid "Select a %s to browse." msgstr "Wähle %s zum Durchsuchen." msgid "Select a period of time to query its usage:" msgstr "Wählen Sie einen Zeitbereich, um die Auslastung abzurufen:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Sell Puppy" msgstr[1] "Sell Puppies" msgid "Sign In" msgstr "Anmelden" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Sold Puppy" msgstr[1] "Sold Puppies" msgid "Submit" msgstr "Abschicken" msgid "Success: " msgstr "Erfolg:" msgid "Summary" msgstr "Zusammenfassung" #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "Das Attribut %(attr)s existiert nicht für %(obj)s." msgid "The date should be in YYYY-mm-dd format." msgstr "Das Datum sollte im YYYY-mm-dd Format sein." msgid "The string may only contain ASCII printable characters." msgstr "Die Zeichenkette darf nur druckbare ASCII-Zeichen enthalten." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "Der Wert von %(resource)s innerhalb der Vorlage ist %(name)s. Der Wert muss " "mit \"http://\" or \"https://\" beginnen, wenn ein Stack von diesem " "Interface gestartet wird." msgid "This Period's GB-Hours:" msgstr "GB-Stunden in diesem Zeitraum:" msgid "This Period's RAM-Hours:" msgstr "RAM-Stunden in diesem Zeitraum:" msgid "This Period's VCPU-Hours:" msgstr "VCPU-Stunden in diesem Zeitraum:" msgid "This action cannot be undone." msgstr "Diese Aktion kann nicht rückgängig gemacht werden." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Nicht möglich: %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Nicht autorisiert. Bitte melden Sie sich erneut an." #, python-format msgid "Unauthorized: %s" msgstr "Nicht berechtigt: %s" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "Eintrag Hochsetzen" msgstr[1] "Einträge Hochsetzen" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "Eintrag aktualisieren" msgstr[1] "Einträge aktualisieren" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "Aktualisierter Eintrag" msgstr[1] "Aktualisierter Einträge" #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "Hochgesetzter Eentrag" msgstr[1] "Hochgesetzte Einträge" msgid "Usage Summary" msgstr "Nutzungsübersicht" msgid "Warning: " msgstr "Warnung:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Sie haben keine Berechtigung für %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Sie sind nicht berechtigt für den Zugriff auf %s" msgid "You do not have permission to access the resource:" msgstr "Sie haben keine Zugriffsrechte auf die Ressource:" horizon-9.0.0/horizon/locale/de/LC_MESSAGES/djangojs.po0000664000567000056710000002130012701407063023610 0ustar jenkinsjenkins00000000000000# Carsten Duch , 2015. #zanata # Carsten Duch , 2016. #zanata # Frank Kloeker , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-12 10:56+0000\n" "Last-Translator: Eugen Block \n" "Language-Team: German\n" "Language: de\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s Bytes" msgid "(Modified)" msgstr "(Verändert)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "Aktiv" msgid "Add Interface" msgstr "Schnittstelle hinzufügen" msgid "Added" msgstr "Hinzugefügt" msgid "Allocated" msgstr "Zugewiesen" msgid "An error occurred while updating." msgstr "Ein Fehler ist beim Aktualisieren aufgetreten." msgid "An error occurred. Please try again later." msgstr "Ein Fehler ist aufgetreten. Bitte versuchen Sie es später noch einmal." msgid "Available" msgstr "Verfügbar" msgid "Available Metadata" msgstr "Verfügbare Metadaten" msgid "Back" msgstr "Zurück" msgid "Cancel" msgstr "Abbrechen" msgid "Click here for filters." msgstr "Klicken Sie hier für Filter." msgid "Click here to expand the row and view the errors." msgstr "Klicken Sie hier um die Zeile zu erweitern und die Fehler zu sehen." msgid "Click to see more details" msgstr "Klicken, um mehr Details zu sehen" msgid "Click to show or hide" msgstr "Klicken zum Zeigen oder Verbergen" msgid "Closed" msgstr "Geschlossen" msgid "Closing" msgstr "Schließvorgang" #, python-format msgid "Confirm %s" msgstr "%s bestätigen" msgid "Confirm Delete Foobars" msgstr "Löschen von Foobars bestätigen" msgid "Connecting" msgstr "Verbindungsaufbau" msgid "Could not decrypt the password" msgstr "Das Passwort konnte nicht entschlüsselt werden" msgid "Could not read the file" msgstr "Die Datei konnte nicht gelesen werden" msgid "Create Subnet" msgstr "Subnetz erstellen" msgid "Current Usage" msgstr "Aktuelle Verwendung" msgid "Custom" msgstr "Anpassen" msgid "Customization Script" msgstr "Anpassungsskript" msgid "Danger" msgstr "Gefahr" msgid "Danger: " msgstr "Gefahr:" msgid "Decimal required" msgstr "Dezimalzahl erforderlich" msgid "Delete" msgstr "Löschen" msgid "Delete Instance" msgstr "Instanz löschen" msgid "Delete Interface" msgstr "Schnittstelle löschen" msgid "Delete Network" msgstr "Lösche Netzwerk" msgid "Delete Router" msgstr "Router löschen" msgid "Delete Subnet" msgstr "Lösche Subnetz" #, python-format msgid "Deleted : %s." msgstr "Gelöscht: %s." msgid "Detail Information" msgstr "Detailinformationen" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "%(count)s von %(total)s Einträge werden angezeigt" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "%s Eintrag wird angezeigt" msgstr[1] "%s Einträge werden angezeigt" msgid "Duplicate keys are not allowed" msgstr "Doppelte Schlüssel sind nicht erlaubt" msgid "Error" msgstr "Fehler" msgid "Error: " msgstr "Fehler:" msgid "Example" msgstr "Beispiel" msgid "Existing Metadata" msgstr "Vorhandene Metadaten" msgid "Expand to see allocated items" msgstr "Erweitern, um zugewiesene Positionen zu sehen" msgid "Expand to see available items" msgstr "Erweitern, um verfügbare Positionen zu sehen" msgid "Filter" msgstr "Filter" msgid "Finish" msgstr "Beenden" msgid "Flavor" msgstr "Variante" msgid "Full Text Search" msgstr "Volltextsuche" msgid "ID" msgstr "KENNUNG" msgid "IP Addresses" msgstr "IP-Addressen" msgid "Info" msgstr "Info" msgid "Integer required" msgstr "Ganzzahl erforderlich" msgid "Interfaces" msgstr "Schnittstellen" msgid "Load script from a file" msgstr "Skript aus einer Datei laden" msgid "Loading" msgstr "Ladevorgang" msgid "Max" msgstr "Max" msgid "Max length" msgstr "Maximale Länge" msgid "Min" msgstr "Min" msgid "Min length" msgstr "Minimale Länge" msgid "Name" msgstr "Name" msgid "Next" msgstr "Weiter" msgid "No" msgstr "Nein" msgid "No Limit" msgstr "Keine Begrenzung" msgid "No available items" msgstr "Keine verfügbaren Einträge" msgid "No available metadata" msgstr "Keine Metadaten vorhanden" msgid "No data available." msgstr "Keine Daten verfügbar." msgid "No description available." msgstr "Keine Beschreibung verfügbar." msgid "No existing metadata" msgstr "Keine existierenden Metadaten" msgid "No items to display." msgstr "Keine Einträge zum Anzeigen." msgid "No roles" msgstr "Keine Rollen" msgid "None" msgstr "Keine" msgid "Not authorized to do this operation." msgstr "Keine Berechtigung für diese Aktion." msgid "Notice: " msgstr "Nachricht:" msgid "Open" msgstr "Öffnen" msgid "Open Console" msgstr "Konsole öffnen" msgid "Passwords do not match." msgstr "Passwörter stimmen nicht überein." msgid "Pattern mismatch" msgstr "Muster-Übereinstimmungsfehler" msgid "Please confirm your selection. " msgstr "Bitte bestätigen Sie Ihre Auswahl." msgid "Prompt" msgstr "Eingabeaufforderung" msgid "Re-order items using drag and drop" msgstr "Umsortieren der Positionen durch Ziehen und Ablegen" msgid "Remaining" msgstr "Verbleibend" msgid "Remove" msgstr "Entfernen" msgid "Required" msgstr "Erforderlich" msgid "Roles" msgstr "Rollen" msgid "STATUS" msgstr "STATUS" #. Strings between {$ and $} should be left untranslated. msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgstr "" "Skriptumfang: {$ (scriptLength || 0) | bytes $} von {$ config." "MAX_SCRIPT_SIZE | bytes $}" msgid "Search in current results" msgstr "Suche in den Ergebnissen" msgid "Select an item from Available items below" msgstr "Wählen Sie einen Eintrag aus den verfügbaren Positionen aus" msgid "Select one" msgstr "Eines auswählen" msgid "Server Name" msgstr "Servername" msgid "Shutdown" msgstr "Herunterfahren" msgid "Status" msgstr "Status" #, python-format msgid "Status: %s" msgstr "Status: %s" msgid "Submit" msgstr "Abschicken" msgid "Subnets" msgstr "Subnetze" msgid "Success" msgstr "Erfolg" msgid "Success: " msgstr "Erfolg:" msgid "Text" msgstr "Text" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "Die Aktion kann nicht ausgeführt werden. Der Inhalt dieser Spalte enthält " "Fehler oder es fehlen Informationen." msgid "The script is larger than the maximum size" msgstr "Das Skript ist größer als die maximal zulässige Größe" msgid "There was a problem communicating with the server, please try again." msgstr "" "Es gab ein Problem bei der Kommunikation mit dem Server. Bitte versuchen Sie " "es noch einmal." msgid "There was an error submitting the form. Please try again." msgstr "" "Es gab einen Fehler beim Abschicken des Formulars. Bitte versuchen Sie es " "noch einmal." msgid "Toggle Dropdown" msgstr "Dropdown-Liste umschalten" msgid "Toggle navigation" msgstr "Navigation umschalten" msgid "Total" msgstr "Total" #, python-format msgid "Unable to delete: %s." msgstr "Löschen nicht möglich: %s." msgid "Unlimited" msgstr "Unbegrenzt" msgid "View Details" msgstr "Details anzeigen" msgid "View Instance Details" msgstr "Instanzdetails anzeigen" msgid "View Router Details" msgstr "Routerdetails anzeigen" msgid "Warning" msgstr "Warnung" msgid "Warning: " msgstr "Warnung:" msgid "Working" msgstr "In Arbeit" msgid "Yes" msgstr "Ja" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "Sie können die Ressourcen-Metadaten angeben, indem Sie Posten von der linken " "in die rechte Spalte bewegen. In der linken Spalte finden Sie Metadaten-" "Definitionen aus dem Glance Metadaten-Katalog. Verwenden Sie die \"Anpassen" "\" Option, um Metadaten mit dem Schlüssel Ihrer Wahl hinzuzufügen." #, python-format msgid "You have selected %s. " msgstr "Sie haben %s ausgewählt." msgid "description" msgstr "Beschreibung" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "Standard-Pool" #, python-format msgid "selected \"%s\"" msgstr "\"%s\" ausgewählt" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || 'Keine Einträge zum Anzeigen.' $}" horizon-9.0.0/horizon/locale/sr/0000775000567000056710000000000012701407231017721 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/sr/LC_MESSAGES/0000775000567000056710000000000012701407231021506 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/sr/LC_MESSAGES/django.po0000664000567000056710000001004112701407063023307 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Serbian\n" "Language: sr\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" "%(end)s" #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s zavrÅ¡eno uspeÅ¡no." #, python-format msgid "%s did not complete." msgstr "%s nije zavrÅ¡en." msgid "-" msgstr "-" msgid "Actions" msgstr "Akcije" msgid "Add a row" msgstr "Dodavanje vrste" msgid "All available" msgstr "Sve dostupno" msgid "Available" msgstr "Dostupno" msgid "Back" msgstr "Nazad" msgid "Cancel" msgstr "Odustani" msgid "Delete" msgstr "Brisanje" msgid "Deleted" msgstr "Obrisano" msgid "Error: " msgstr "GreÅ¡ka:" msgid "Filter" msgstr "Filter" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Neispravan format za IP adresu" msgid "Info: " msgstr "Info:" msgid "Invalid subnet mask" msgstr "Nevažeća maska podmreže" msgid "Invalid version for IP address" msgstr "Neispravna verzija IP adrese" msgid "Limit Summary" msgstr "Rezime ograniÄenja" msgid "Login" msgstr "Login" msgid "Members" msgstr "ÄŒlanovi" msgid "Navigation Item" msgstr "Stavka za navigaciju" msgid "Never" msgstr "Nikad" msgid "Next" msgstr "Sledeći" msgid "No items to display." msgstr "Nema stavki za prikaz." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Ne postoji podudaranje za id \"%s\"." msgid "No members." msgstr "Nema Älanova." msgid "None available." msgstr "NiÅ¡ta nije dostupno." msgid "Not a valid IP protocol number" msgstr "Nevažeći broj IP protokola" msgid "Not a valid port number" msgstr "Nevažeći broj porta" msgid "One colon allowed in port range" msgstr "Jedna dvotaÄka je dozvoljena u opsegu portova" msgid "Other" msgstr "Drugi" msgid "Password is not accepted" msgstr "Lozinka nije prihvaćena" msgid "Please log in to continue." msgstr "Molim, ulogujte se za nastavak." msgid "Please select a row before taking that action." msgstr "Molim odaberite vrstu pre nego poÄnete." msgid "Processing..." msgstr "Obrada..." msgid "Save" msgstr "SaÄuvaj" #, python-format msgid "Select a %s to browse." msgstr "Odaberite %s za pregledanje." msgid "Sign In" msgstr "Pristup" msgid "Submit" msgstr "Predaj" msgid "Success: " msgstr "Uspeh:" msgid "Summary" msgstr "Rezime" msgid "The date should be in YYYY-mm-dd format." msgstr "Format datuma treba da bude GGGG-mm-dd." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Nemoguće je %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Niste ovlašćeni. Molim, pokuÅ¡ajte da se ulogujete ponovo." #, python-format msgid "Unauthorized: %s" msgstr "Neovlašćen: %s" msgid "Usage Summary" msgstr "Rezime korišćenja" msgid "Warning: " msgstr "Upozorenje:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Nije vam dozvoljeno da %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Niste ovlašćeni za pristup %s" msgid "You do not have permission to access the resource:" msgstr "Nemate dozvolu za pristup resursu:" horizon-9.0.0/horizon/locale/sr/LC_MESSAGES/djangojs.po0000664000567000056710000000567312701407063023663 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Serbian\n" "Language: sr\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" msgid "Add Interface" msgstr "Dodavanje interface-a" msgid "Added" msgstr "Dodato" msgid "An error occurred while updating." msgstr "GreÅ¡ka u toku ažuriranja." msgid "An error occurred. Please try again later." msgstr "GreÅ¡ka. PokuÅ¡ajte ponovo kasnije." msgid "Available" msgstr "Dostupno" msgid "Available Metadata" msgstr "Dostupni Metadata podaci" msgid "Back" msgstr "Nazad" msgid "Cancel" msgstr "Odustani" #, python-format msgid "Confirm %s" msgstr "Potvrda %s" msgid "Could not decrypt the password" msgstr "Ne mogu deÅ¡ifrovati lozinku" msgid "Could not read the file" msgstr "Ne mogu da proÄitam fajl" msgid "Danger: " msgstr "Opasnost:" msgid "Delete" msgstr "Brisanje" msgid "Duplicate keys are not allowed" msgstr "Dupli kljucevi nisu omoguceni" msgid "Error" msgstr "GreÅ¡ka" msgid "Error: " msgstr "GreÅ¡ka:" msgid "Existing Metadata" msgstr "Postojeci Metadata podaci" msgid "Filter" msgstr "Filter" msgid "Flavor" msgstr "Aroma" msgid "ID" msgstr "ID" msgid "Info" msgstr "Informacije" msgid "Interfaces" msgstr "Interfejsi" msgid "Loading" msgstr "UÄitavanje" msgid "Name" msgstr "Ime" msgid "Next" msgstr "Sledeći" msgid "No data available." msgstr "Nema podataka." msgid "No existing metadata" msgstr "Nema postojecih metadata podataka" msgid "No items to display." msgstr "Nema stavki za prikaz." msgid "No roles" msgstr "Nema uloga" msgid "None" msgstr "NiÅ¡ta" msgid "Not authorized to do this operation." msgstr "Niste nadležni za ovaj zahvat." msgid "Notice: " msgstr "UoÄite:" msgid "Passwords do not match." msgstr "Lozinka se ne podudara." msgid "Remove" msgstr "Ukloni" msgid "Roles" msgstr "Uloge" msgid "STATUS" msgstr "STATUS" msgid "Status" msgstr "Status" msgid "Submit" msgstr "Predaj" msgid "Success: " msgstr "Uspeh:" msgid "There was a problem communicating with the server, please try again." msgstr "Postojao je problem u vezi sa serverom, pokuÅ¡ajte ponovo." msgid "There was an error submitting the form. Please try again." msgstr "GreÅ¡ka u predaji forme. PokuÅ¡ajte ponovo." msgid "View Details" msgstr "Pregled detalja" msgid "Warning: " msgstr "Upozorenje:" msgid "Working" msgstr "Obrada u toku" #, python-format msgid "You have selected %s. " msgstr "Odabrali ste %s. " horizon-9.0.0/horizon/locale/id/0000775000567000056710000000000012701407231017671 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/id/LC_MESSAGES/0000775000567000056710000000000012701407231021456 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/id/LC_MESSAGES/django.po0000664000567000056710000001361612701407063023272 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Indonesian\n" "Language: id\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" "%(end)s" #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Byte" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s berhasil diselesaikan." #, python-format msgid "%s did not complete." msgstr "%s tidak selesai." msgid "« Prev" msgstr "« Mundur" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 Bytes" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Sebuah %(resource)s dengan nama \"%(name)s\" sudah ada." msgid "Actions" msgstr "Aksi" msgid "Active Instances:" msgstr "Instance aktif" msgid "Active RAM:" msgstr "RAM aktif:" msgid "Add a row" msgstr "Tambah baris" msgid "All available" msgstr "Semua yang tersed1a" msgid "Available" msgstr "Tersedia" msgid "Back" msgstr "Kembali" msgid "Cancel" msgstr "Batal" msgid "Delete" msgstr "Hapus" msgid "Deleted" msgstr "Terhapus" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Menampilkan sebanyak %(content_items)s item" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Menampilkan %(counter)s item" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Tampilkan %(nav_items)s items" msgid "Error: " msgstr "Kesalahan:" msgid "Fake" msgstr "Bohongan" msgid "Filter" msgstr "Penyaring" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Format alamat IP salah." msgid "Info: " msgstr "Informasi:" msgid "Invalid subnet mask" msgstr "Subnet mask alamat IP tidak benar." msgid "Invalid version for IP address" msgstr "Versi alamat IP salah." msgid "Limit Summary" msgstr "Ringkasan Batasan" msgid "Login" msgstr "Login" msgid "Members" msgstr "Anggota" msgid "More Actions" msgstr "Aksi Lebih" msgid "Navigation Item" msgstr "Item Navigasi" msgid "Never" msgstr "Tidak pernah" msgid "Next" msgstr "Berikutnya" msgid "Next »" msgstr "Berikut »" msgid "No items to display." msgstr "Tidak ada yang ditampilkan" #, python-format msgid "No match returned for the id \"%s\"." msgstr "Tidak ada hasil yang terhubung dengan id \"%s\"." msgid "No members." msgstr "Bukan anggota." msgid "None available." msgstr "Tidak satupun tersedia" msgid "Not a valid IP protocol number" msgstr "Nomor IP yang salah" msgid "Not a valid port number" msgstr "Nomor port yang salah" msgid "One colon allowed in port range" msgstr "Diijinkan sebuah titik dua dalam jangkauan port" msgid "Other" msgstr "Lainnya" msgid "Password is not accepted" msgstr "Kata kunci ditolak" msgid "Please log in to continue." msgstr "Harap masuk untuk melanjutkan" msgid "Please select a row before taking that action." msgstr "Mohon pilih sebuah baris terlebih dahulu sebelum aksi." msgid "Processing..." msgstr "Sedang di proses..." msgid "Save" msgstr "Simpan" #, python-format msgid "Select a %s to browse." msgstr "Pilih sebuah %s untuk menelusuri." msgid "Select a period of time to query its usage:" msgstr "Pilih sebuah periode waktu untuk mencari kegunaannya:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Menjual Puppies" msgid "Sign In" msgstr "Mendaftar" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Puppies terjual" msgid "Submit" msgstr "Kirim" msgid "Success: " msgstr "Berhasil:" msgid "Summary" msgstr "Ringkasan" msgid "The date should be in YYYY-mm-dd format." msgstr "Format tanggal harus berupa YYYY-mm-dd" msgid "This Period's GB-Hours:" msgstr "GB-Hours saat ini" msgid "This Period's RAM-Hours:" msgstr "RAM-Hours periode ini:" msgid "This Period's VCPU-Hours:" msgstr "VCPU-Hours periode ini:" msgid "This action cannot be undone." msgstr "Aksi ini tidak dapat diselesaikan" #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Tidak dapat %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Tidak Diijinkan. Silahkan login kembali." #, python-format msgid "Unauthorized: %s" msgstr "Tidak Diijinkan: %s" msgid "Usage Summary" msgstr "Ringkasan Penggunaan" msgid "Warning: " msgstr "Peringatan:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Anda tidak diijinkan untuk %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Anda tidak diijinkan untuk mengakses %s" msgid "You do not have permission to access the resource:" msgstr "Anda tidak diijinkan untuk mengakses bagian ini." horizon-9.0.0/horizon/locale/fr/0000775000567000056710000000000012701407231017704 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/fr/LC_MESSAGES/0000775000567000056710000000000012701407231021471 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/fr/LC_MESSAGES/django.po0000664000567000056710000002374512701407071023310 0ustar jenkinsjenkins00000000000000# Kevin Tibi , 2015. #zanata # JF Taltavull , 2016. #zanata # Kevin Tibi , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-28 06:42+0000\n" "Last-Translator: JF Taltavull \n" "Language-Team: French\n" "Language: fr\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " Connectez-vous avec un autre nom d'utilisateur ou revenez à " "home page\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " %(used)s utilisé(es) (Pas de limite)\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " %(used)s sur %(available)s " "utilisé(es)\n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" "%(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " Si vous n'êtes pas sûr de la méthode d'authentification à utiliser, " "veuillez contacter votre administrateur.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s : %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s : " #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s : %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Octet" msgstr[1] "%(size)d Octets" #, python-format msgid "%s GB" msgstr "%s Go" #, python-format msgid "%s KB" msgstr "%s Ko" #, python-format msgid "%s MB" msgstr "%s Mo" #, python-format msgid "%s PB" msgstr "%s Po" #, python-format msgid "%s TB" msgstr "%s To" #, python-format msgid "%s completed successfully." msgstr "%s terminé avec succès." #, python-format msgid "%s did not complete." msgstr "%s ne s'est pas terminé." msgid "« Prev" msgstr "« Précédent" msgid "(No Limit)" msgstr "(Pas de limite)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 Octets" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Un(e) %(resource)s avec le nom \"%(name)s\" existe déjà." msgid "Actions" msgstr "Actions" msgid "Active Instances:" msgstr "Instances Actives :" msgid "Active RAM:" msgstr "RAM Active :" msgid "Add a row" msgstr "Ajouter une ligne" msgid "All available" msgstr "Disponibles" msgid "Available" msgstr "Disponible" msgid "Back" msgstr "Retour" #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "Batch Item" msgstr[1] "Batch Items" #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "Batched Item" msgstr[1] "Batched Items" msgid "Cancel" msgstr "Annuler" msgid "Connect" msgstr "Connecter" msgid "Delete" msgstr "Supprimer" msgid "Deleted" msgstr "Supprimé(e)" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "Affichage de l'élément %(content_items)s" msgstr[1] "Affichage des éléments %(content_items)s" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "Affichage de %(counter)s item" msgstr[1] "Affichage de %(counter)s items" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "Affichage de l'élément %(nav_items)s" msgstr[1] "Affichage des éléments %(nav_items)s" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "Descendre l'élément" msgstr[1] "Descendre les éléments" #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "Élément descendu" msgstr[1] "Éléments descendus" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "Erreur lors du traitement du fichier json '%(path)s': %(exception)s" msgid "Error: " msgstr "Erreur :" msgid "Fake" msgstr "Faux" msgid "Filter" msgstr "Filtrer" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Format d'adresse IP incorrect" msgid "Info: " msgstr "Information :" msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "" "La saisie des métadonnées est non valide. Utiliser les paires clé=valeur " "séparées par des virgules" msgid "Invalid subnet mask" msgstr "Masque de sous-réseau invalide" msgid "Invalid version for IP address" msgstr "Version d'adresse IP invalide" msgid "Limit Summary" msgstr "Synthèse des Quotas" msgid "Log in" msgstr "Se connecter" msgid "Login" msgstr "Identifiant" msgid "Members" msgstr "Membres" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "" "Le format du fichier de message json '%(path)s' est incorrect. %(exception)s" msgid "More Actions" msgstr "Plus d'actions" msgid "Navigation Item" msgstr "Elément de navigation" msgid "Never" msgstr "Jamais" msgid "Next" msgstr "Suivant" msgid "Next »" msgstr "Suivant »" msgid "No items to display." msgstr "Aucun élément à afficher." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Aucun résultat retourné pour l'id \"%s\"." msgid "No members." msgstr "Aucun membre." msgid "None available." msgstr "Aucun disponible." msgid "Not a valid IP protocol number" msgstr "Numéro de protocole IP invalide " msgid "Not a valid port number" msgstr "Numéro de port invalide" msgid "One colon allowed in port range" msgstr "Un seul caractère deux-points autorisé dans une plage de ports" msgid "Other" msgstr "Autre" msgid "Password is not accepted" msgstr "Le mot de passe n'est pas accepté" msgid "Please log in to continue." msgstr "Merci de vous connecter pour continuer." msgid "Please select a row before taking that action." msgstr "Merci de sélectionner une ligne avant de faire cette action." msgid "Processing..." msgstr "Traitement en cours..." msgid "Save" msgstr "Enregistrer" #, python-format msgid "Select a %s to browse." msgstr "Sélectionner un(e) %s à parcourir." msgid "Select a period of time to query its usage:" msgstr "Sélectionnez une période de temps pour connaitre son utilisation :" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Chiot à vendre" msgstr[1] "Chiots à vendre" msgid "Sign In" msgstr "Se connecter" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Chiot vendu" msgstr[1] "Chiots Vendus" msgid "Submit" msgstr "Envoyer" msgid "Success: " msgstr "Succès :" msgid "Summary" msgstr "Résumé" #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "L'attribut %(attr)s de l'objet %(obj)s n'existe pas." msgid "The date should be in YYYY-mm-dd format." msgstr "La date doit être au format AAAA-mm-jj" msgid "The string may only contain ASCII printable characters." msgstr "La chaîne ne peut contenir que des caractères ASCII imprimables." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "La valeur de %(resource)s est %(name)s à l'intérieur du template. Quand vous " "lancez une stack depuis l'interface, la valeur doit commencer par \"http://" "\" ou \"https://\"" msgid "This Period's GB-Hours:" msgstr "GB-Heures de cette période :" msgid "This Period's RAM-Hours:" msgstr "RAM-Heures de cette période :" msgid "This Period's VCPU-Hours:" msgstr "VCPU-Heures de cette Période :" msgid "This action cannot be undone." msgstr "Cette action ne peut pas être défaite." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Impossible de %(action)s : %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Accès non autorisé. Merci de vous reconnecter." #, python-format msgid "Unauthorized: %s" msgstr "%s : non autorisé" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "Remonter l'élément" msgstr[1] "Remonter les éléments" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "Mise à jour de l'élément" msgstr[1] "Mise à jour des éléments" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "Élément mis à jour" msgstr[1] "Éléments mis à jour" #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "Élément remonté" msgstr[1] "Éléments remontés" msgid "Usage Summary" msgstr "Résumé de l'Utilisation" msgid "Warning: " msgstr "Avertissement :" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Vous n'êtes pas autorisé à %(action)s : %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Vous n'êtes pas autorisé à accéder à %s" msgid "You do not have permission to access the resource:" msgstr "Vous n'avez pas la permission d'accéder à la ressource :" horizon-9.0.0/horizon/locale/fr/LC_MESSAGES/djangojs.po0000664000567000056710000002237012701407063023637 0ustar jenkinsjenkins00000000000000# Corinne Verheyde , 2015. #zanata # François Bureau , 2015. #zanata # Gael Rehault , 2015. #zanata # Maxime Coquerel , 2015. #zanata # Olivier Buisson , 2015. #zanata # OpenStack Infra , 2015. #zanata # Romain Chantereau , 2015. #zanata # Corinne Verheyde , 2016. #zanata # Gérald LONLAS , 2016. #zanata # JF Taltavull , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-07 10:04+0000\n" "Last-Translator: Gérald LONLAS \n" "Language-Team: French\n" "Language: fr\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" #, python-format msgid "%s GB" msgstr "%s Go" #, python-format msgid "%s KB" msgstr "%s Ko" #, python-format msgid "%s MB" msgstr "%s Mo" #, python-format msgid "%s TB" msgstr "%s To" #, python-format msgid "%s bytes" msgstr "%s octets" msgid "(Modified)" msgstr "(Modifié)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 Go" msgid "0 MB" msgstr "0 Mo" msgid "Active" msgstr "Active" msgid "Add Interface" msgstr "Ajouter une interface" msgid "Added" msgstr "Ajouté" msgid "Allocated" msgstr "Alloué" msgid "An error occurred while updating." msgstr "Une erreur s'est produite durant la mise à jour." msgid "An error occurred. Please try again later." msgstr "Une erreur s'est produite. Veuillez réessayer ultérieurement." msgid "Available" msgstr "Disponible(s)" msgid "Available Metadata" msgstr "Métadonnées disponibles" msgid "Back" msgstr "Retour" msgid "Cancel" msgstr "Annuler" msgid "Click here for filters." msgstr "Cliquer pour filtrer." msgid "Click here to expand the row and view the errors." msgstr "Cliquer ici pour développer cette ligne et consulter les erreurs." msgid "Click to see more details" msgstr "Cliquer pour une vue plus détaillée" msgid "Click to show or hide" msgstr "Cliquer pour montrer ou cacher" msgid "Closed" msgstr "Fermé" msgid "Closing" msgstr "En cours de fermeture" #, python-format msgid "Confirm %s" msgstr "Confirmez %s" msgid "Confirm Delete Foobars" msgstr "Confirmer la suppression de Foobars" msgid "Connecting" msgstr "En cours de connexion" msgid "Could not decrypt the password" msgstr "Le mot de passe n'a pu être déchiffré" msgid "Could not read the file" msgstr "Le fichier n'a pu être lu" msgid "Create Subnet" msgstr "Créer un sous-réseau" msgid "Current Usage" msgstr "Utilisation actuelle" msgid "Custom" msgstr "Personnaliser" msgid "Customization Script" msgstr "Script de Personnalisation" msgid "Danger" msgstr "Danger" msgid "Danger: " msgstr "Danger :" msgid "Decimal required" msgstr "Nombre décimal requis" msgid "Delete" msgstr "Supprimer" msgid "Delete Instance" msgstr "Supprimer l'instance " msgid "Delete Interface" msgstr "Supprimer l'Interface" msgid "Delete Network" msgstr "Supprimer le réseau" msgid "Delete Router" msgstr "Supprimer un Routeur" msgid "Delete Subnet" msgstr "Supprimer le sous-réseau" #, python-format msgid "Deleted : %s." msgstr "Supprimé : %s." msgid "Detail Information" msgstr "Informations détaillées" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "Affichage de %(count)s éléments sur %(total)s" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "Affichage de %s élément" msgstr[1] "Affichage de %s éléments" msgid "Duplicate keys are not allowed" msgstr "Les clés dupliquées ne sont pas autorisées" msgid "Error" msgstr "Erreur" msgid "Error: " msgstr "Erreur :" msgid "Example" msgstr "Exemple" msgid "Existing Metadata" msgstr "Métadonnées existantes" msgid "Expand to see allocated items" msgstr "Développer pour voir les éléments alloués" msgid "Expand to see available items" msgstr "Développer pour voir les éléments disponibles" msgid "Filter" msgstr "Filtrer" msgid "Finish" msgstr "Terminer" msgid "Flavor" msgstr "Gabarit" msgid "Full Text Search" msgstr "Recherche en texte intégral" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "Adresses IP" msgid "Info" msgstr "Informations" msgid "Integer required" msgstr "Entier requis" msgid "Interfaces" msgstr "Interfaces" msgid "Load script from a file" msgstr "Charger un script depuis un fichier" msgid "Loading" msgstr "Chargement..." msgid "Max" msgstr "Max" msgid "Max length" msgstr "Longueur max" msgid "Min" msgstr "Min" msgid "Min length" msgstr "Longueur mini" msgid "Name" msgstr "Nom" msgid "Next" msgstr "Suivant" msgid "No" msgstr "Non" msgid "No Limit" msgstr "Aucune limite" msgid "No available items" msgstr "Pas d'élément disponible" msgid "No available metadata" msgstr "Pas de métadonnées disponibles" msgid "No data available." msgstr "Pas de données disponibles." msgid "No description available." msgstr "Pas de description disponible." msgid "No existing metadata" msgstr "Pas de métadonnées existantes" msgid "No items to display." msgstr "Aucun élément à afficher." msgid "No roles" msgstr "Aucun rôle" msgid "None" msgstr "Aucun" msgid "Not authorized to do this operation." msgstr "Vous n'êtes pas autorisé à effectuer cette opération." msgid "Notice: " msgstr "Notification :" msgid "Open" msgstr "Ouvert" msgid "Open Console" msgstr "Ouvrir la Console" msgid "Passwords do not match." msgstr "Les mots de passe ne correspondent pas." msgid "Pattern mismatch" msgstr "Modèle ne correspondant pas" msgid "Please confirm your selection. " msgstr "Merci de confirmer votre sélection." msgid "Prompt" msgstr "Invite de commande" msgid "Re-order items using drag and drop" msgstr "" "Ordonnancer les éléments à nouveau en utilisant l'opération glisser-déplacer" msgid "Remaining" msgstr "Restant" msgid "Remove" msgstr "Supprimer" msgid "Required" msgstr "Requis" msgid "Roles" msgstr "Rôles" msgid "STATUS" msgstr "STATUT" #. Strings between {$ and $} should be left untranslated. msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgstr "" "Taille du script : {$ (scriptLength || 0) | bytes $} sur {$ config." "MAX_SCRIPT_SIZE | bytes $}" msgid "Search in current results" msgstr "Recherche dans les résultats en cours" msgid "Select an item from Available items below" msgstr "Sélectionner un élément depuis les éléments disponibles ci-dessous" msgid "Select one" msgstr "Sélectionner un" msgid "Server Name" msgstr "Nom du serveur" msgid "Shutdown" msgstr "Éteindre " msgid "Status" msgstr "Statut" #, python-format msgid "Status: %s" msgstr "État : %s" msgid "Submit" msgstr "Envoyer" msgid "Subnets" msgstr "Sous-réseaux" msgid "Success" msgstr "Succès " msgid "Success: " msgstr "Succès :" msgid "Text" msgstr "Texte" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "Cette action ne peut pas être effectuée. Le contenu de cette ligne contient " "des erreurs ou manque d'informations." msgid "The script is larger than the maximum size" msgstr "Le script est plus lourd que la poids maximum" msgid "There was a problem communicating with the server, please try again." msgstr "Problème de communication avec le serveur, veuillez réessayer." msgid "There was an error submitting the form. Please try again." msgstr "Erreur lors de la soumission du formulaire. Veuillez réessayer. " msgid "Toggle Dropdown" msgstr "Activation du menu déroulant" msgid "Toggle navigation" msgstr "Changer la navigation" msgid "Total" msgstr "Total" #, python-format msgid "Unable to delete: %s." msgstr "Impossible de supprimer: %s." msgid "Unlimited" msgstr "Ilimité" msgid "View Details" msgstr "Voir les détails" msgid "View Instance Details" msgstr "Voir les Détails de l'Instance" msgid "View Router Details" msgstr "Voir les Détails du Routeur" msgid "Warning" msgstr "Avertissement" msgid "Warning: " msgstr "Avertissement :" msgid "Working" msgstr "Traitement en cours..." msgid "Yes" msgstr "Oui" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "Vous pouvez spécifier les ressources de métadonnées en déplaçant les " "éléments de la colonne de gauche vers la colonne de droite. La colonne de " "gauche contient des définitions de métadonnées du Catalogue de métadonnées " "Glance. Utiliser l'option \"Autre\" pour ajouter des métadonnées avec la clé " "de votre choix." #, python-format msgid "You have selected %s. " msgstr "Vous avez sélectionné %s." msgid "description" msgstr "description " msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "pool par défaut" #, python-format msgid "selected \"%s\"" msgstr "Vous avez sélectionné \"%s\"" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || 'Aucun élément à afficher.' $}" horizon-9.0.0/horizon/locale/django.pot0000664000567000056710000002661612701407063021301 0ustar jenkinsjenkins00000000000000# Translations template for PROJECT. # Copyright (C) 2016 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # FIRST AUTHOR , 2016. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-16 06:14+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.2.0\n" #: horizon/base.py:500 msgid "Other" msgstr "" #: horizon/decorators.py:53 msgid "Please log in to continue." msgstr "" #: horizon/decorators.py:85 #, python-format msgid "You are not authorized to access %s" msgstr "" #: horizon/exceptions.py:119 #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "" #: horizon/exceptions.py:135 #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching" " a stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" #: horizon/exceptions.py:216 #, python-format msgid "Unauthorized: %s" msgstr "" #: horizon/exceptions.py:219 horizon/middleware.py:130 msgid "Unauthorized. Please try logging in again." msgstr "" #: horizon/notifications.py:69 #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "" #: horizon/notifications.py:92 #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "" #: horizon/browsers/base.py:88 msgid "Navigation Item" msgstr "" #: horizon/browsers/views.py:41 #, python-format msgid "Select a %s to browse." msgstr "" #: horizon/conf/default.py:44 msgid "Password is not accepted" msgstr "" #: horizon/forms/fields.py:66 msgid "Incorrect format for IP address" msgstr "" #: horizon/forms/fields.py:67 msgid "Invalid version for IP address" msgstr "" #: horizon/forms/fields.py:68 msgid "Invalid subnet mask" msgstr "" #: horizon/forms/views.py:132 #: horizon/templates/horizon/common/_usage_summary.html:16 msgid "Submit" msgstr "" #: horizon/forms/views.py:133 #: horizon/templates/horizon/common/_workflow.html:57 msgid "Cancel" msgstr "" #: horizon/tables/actions.py:459 #: horizon/templates/horizon/common/_data_table_table_actions.html:21 #: horizon/templates/horizon/common/_data_table_table_actions.html:33 #: horizon/templates/horizon/common/_workflow_step_update_members.html:14 #: horizon/templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" msgstr "" #: horizon/tables/actions.py:655 msgid "This action cannot be undone." msgstr "" #: horizon/tables/actions.py:777 #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "" #: horizon/tables/actions.py:779 #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "" #: horizon/tables/actions.py:842 #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "" #: horizon/tables/actions.py:849 #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "" #: horizon/tables/actions.py:855 #, python-format msgid "%(action)s: %(objs)s" msgstr "" #: horizon/tables/actions.py:926 msgid "Delete" msgstr "" #: horizon/tables/actions.py:928 msgid "Deleted" msgstr "" #: horizon/tables/actions.py:965 msgid "Update Item" msgid_plural "Update Items" msgstr[0] "" msgstr[1] "" #: horizon/tables/actions.py:973 msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "" msgstr[1] "" #: horizon/tables/base.py:307 msgid "-" msgstr "" #: horizon/tables/base.py:362 #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "" #: horizon/tables/base.py:1001 msgid "No items to display." msgstr "" #: horizon/tables/base.py:1113 #: horizon/templates/horizon/common/_data_table_table_actions.html:56 msgid "Actions" msgstr "" #: horizon/tables/base.py:1348 #, python-format msgid "No match returned for the id \"%s\"." msgstr "" #: horizon/tables/base.py:1506 msgid "Please select a row before taking that action." msgstr "" #: horizon/templates/auth/_description.html:11 msgid "" "\n" " If you are not sure which authentication method to use, contact " "your administrator.\n" " " msgstr "" #: horizon/templates/auth/_login_form.html:17 msgid "Log in" msgstr "" #: horizon/templates/auth/_login_form.html:38 msgid "You do not have permission to access the resource:" msgstr "" #: horizon/templates/auth/_login_form.html:49 #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" #: horizon/templates/auth/_login_form.html:69 msgid "Sign In" msgstr "" #: horizon/templates/auth/_login_form.html:70 msgid "Connect" msgstr "" #: horizon/templates/auth/login.html:4 msgid "Login" msgstr "" #: horizon/templates/horizon/_messages.html:10 msgid "Info: " msgstr "" #: horizon/templates/horizon/_messages.html:18 msgid "Warning: " msgstr "" #: horizon/templates/horizon/_messages.html:26 msgid "Success: " msgstr "" #: horizon/templates/horizon/_messages.html:34 msgid "Error: " msgstr "" #: horizon/templates/horizon/common/_data_table.html:61 msgid "Summary" msgstr "" #: horizon/templates/horizon/common/_data_table.html:70 #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "" msgstr[1] "" #: horizon/templates/horizon/common/_data_table.html:75 msgid "« Prev" msgstr "" #: horizon/templates/horizon/common/_data_table.html:78 msgid "Next »" msgstr "" #: horizon/templates/horizon/common/_data_table_table_actions.html:54 msgid "More Actions" msgstr "" #: horizon/templates/horizon/common/_domain_page_header.html:6 #, python-format msgid "%(context_name)s:" msgstr "" #: horizon/templates/horizon/common/_formset_table.html:35 msgid "Add a row" msgstr "" #: horizon/templates/horizon/common/_formset_table_row.html:15 #, python-format msgid "%(name)s: %(error)s" msgstr "" #: horizon/templates/horizon/common/_limit_summary.html:5 msgid "Limit Summary" msgstr "" #: horizon/templates/horizon/common/_limit_summary.html:20 #, python-format msgid "" "\n" " Used %(used)s of %(available)s " "\n" " " msgstr "" #: horizon/templates/horizon/common/_limit_summary.html:24 #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" #: horizon/templates/horizon/common/_resource_browser.html:10 #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "" msgstr[1] "" #: horizon/templates/horizon/common/_resource_browser.html:11 #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "" msgstr[1] "" #: horizon/templates/horizon/common/_usage_summary.html:3 msgid "Usage Summary" msgstr "" #: horizon/templates/horizon/common/_usage_summary.html:7 msgid "Select a period of time to query its usage:" msgstr "" #: horizon/templates/horizon/common/_usage_summary.html:10 #, python-format msgid "" "\n" " %(start)s" msgstr "" #: horizon/templates/horizon/common/_usage_summary.html:14 #, python-format msgid "" "\n" " %(end)s" msgstr "" #: horizon/templates/horizon/common/_usage_summary.html:17 msgid "The date should be in YYYY-mm-dd format." msgstr "" #: horizon/templates/horizon/common/_usage_summary.html:20 msgid "Active Instances:" msgstr "" #: horizon/templates/horizon/common/_usage_summary.html:21 msgid "Active RAM:" msgstr "" #: horizon/templates/horizon/common/_usage_summary.html:22 msgid "This Period's VCPU-Hours:" msgstr "" #: horizon/templates/horizon/common/_usage_summary.html:23 msgid "This Period's GB-Hours:" msgstr "" #: horizon/templates/horizon/common/_usage_summary.html:24 msgid "This Period's RAM-Hours:" msgstr "" #: horizon/templates/horizon/common/_workflow.html:49 msgid "Back" msgstr "" #: horizon/templates/horizon/common/_workflow.html:52 msgid "Next" msgstr "" #: horizon/templatetags/branding.py:34 msgid "Horizon" msgstr "" #: horizon/templatetags/horizon.py:148 msgid "(No Limit)" msgstr "" #: horizon/templatetags/horizon.py:151 horizon/templatetags/horizon.py:153 msgid "Available" msgstr "" #: horizon/templatetags/sizeformat.py:51 horizon/templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "" msgstr[1] "" #: horizon/templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "" #: horizon/templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "" #: horizon/templatetags/sizeformat.py:63 #, python-format msgid "%s GB" msgstr "" #: horizon/templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "" #: horizon/templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "" #: horizon/templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" #. Translators: test code, don't really have to translate #: horizon/test/test_dashboards/dogs/puppies/tables.py:30 msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "" msgstr[1] "" #. Translators: test code, don't really have to translate #: horizon/test/test_dashboards/dogs/puppies/tables.py:39 msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "" msgstr[1] "" #. Translators: test code, don't really have to translate #: horizon/test/tests/tables.py:139 msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "" msgstr[1] "" #. Translators: test code, don't really have to translate #: horizon/test/tests/tables.py:148 msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "" msgstr[1] "" #. Translators: test code, don't really have to translate #: horizon/test/tests/tables.py:176 msgid "Up Item" msgid_plural "Up Items" msgstr[0] "" msgstr[1] "" #. Translators: test code, don't really have to translate #: horizon/test/tests/tables.py:183 msgid "Down Item" msgid_plural "Down Items" msgstr[0] "" msgstr[1] "" #. Translators: test code, don't really have to translate #: horizon/test/tests/tables.py:192 msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "" msgstr[1] "" #. Translators: test code, don't really have to translate #: horizon/test/tests/tables.py:199 msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "" msgstr[1] "" #: horizon/test/tests/views.py:59 msgid "Fake" msgstr "" #: horizon/utils/filters.py:49 msgid "Never" msgstr "" #: horizon/utils/validators.py:28 msgid "Not a valid port number" msgstr "" #: horizon/utils/validators.py:33 msgid "Not a valid IP protocol number" msgstr "" #: horizon/utils/validators.py:47 msgid "One colon allowed in port range" msgstr "" #: horizon/utils/validators.py:54 msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "" #: horizon/utils/validators.py:70 msgid "The string may only contain ASCII printable characters." msgstr "" #: horizon/workflows/base.py:71 msgid "Processing..." msgstr "" #: horizon/workflows/base.py:477 msgid "All available" msgstr "" #: horizon/workflows/base.py:478 msgid "Members" msgstr "" #: horizon/workflows/base.py:479 msgid "None available." msgstr "" #: horizon/workflows/base.py:480 msgid "No members." msgstr "" #: horizon/workflows/base.py:597 msgid "Save" msgstr "" #: horizon/workflows/base.py:598 #, python-format msgid "%s completed successfully." msgstr "" #: horizon/workflows/base.py:599 #, python-format msgid "%s did not complete." msgstr "" horizon-9.0.0/horizon/locale/zh_TW/0000775000567000056710000000000012701407231020330 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/zh_TW/LC_MESSAGES/0000775000567000056710000000000012701407231022115 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/zh_TW/LC_MESSAGES/django.po0000664000567000056710000002166112701407071023727 0ustar jenkinsjenkins00000000000000# Zhang Xiaowei , 2015. #zanata # Ching Kuo , 2016. #zanata # Jennifer , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-22 09:40+0000\n" "Last-Translator: Jennifer \n" "Language-Team: Chinese (Taiwan)\n" "Language: zh-TW\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " 以ä¸åŒçš„用戶身份登入,或者回到首é \n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " 已使用 %(used)s (無é™åˆ¶ï¼‰\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " 已使用 %(available)s 個中的 %(used)s 個\n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " 若您ä¸ç¢ºå®šè¦ç”¨å“ªå€‹é©—證方法,請è¯çµ¡æ‚¨çš„管ç†å“¡ã€‚\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s:%(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s:%(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d ä½å…ƒçµ„" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "å·²æˆåŠŸåœ°å®Œæˆ %s。" #, python-format msgid "%s did not complete." msgstr "å°šæœªå®Œæˆ %s。" msgid "« Prev" msgstr "« ä¸Šä¸€é " msgid "(No Limit)" msgstr "(無é™åˆ¶ï¼‰" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 ä½å…ƒçµ„" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "å為「%(name)sã€çš„ %(resource)s 已經存在。" msgid "Actions" msgstr "動作" msgid "Active Instances:" msgstr "使用中的執行個體:" msgid "Active RAM:" msgstr "使用中的隨機存å–記憶體:" msgid "Add a row" msgstr "加入一列" msgid "All available" msgstr "全部å¯ç”¨" msgid "Available" msgstr "å¯ç”¨" msgid "Back" msgstr "上一é " #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "Batch Item" #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "Batched Item" msgid "Cancel" msgstr "å–æ¶ˆ" msgid "Connect" msgstr "連接" msgid "Delete" msgstr "刪除" msgid "Deleted" msgstr "已刪除" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "顯示 %(content_items)s é …" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "顯示 %(counter)s é …" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "顯示 %(nav_items)s é …" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "Down Item" #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "Downed Item" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "處ç†è¨Šæ¯ JSON 檔 '%(path)s' 時發生錯誤:%(exception)s" msgid "Error: " msgstr "錯誤:" msgid "Fake" msgstr "å‡å†’" msgid "Filter" msgstr "篩é¸" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "䏿­£ç¢ºçš„ IP ä½å€æ ¼å¼" msgid "Info: " msgstr "資訊:" msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "無效的詮釋資料æ¢ç›®ã€‚使用以逗號分隔的 éµ=值 å°" msgid "Invalid subnet mask" msgstr "無效的å­ç¶²è·¯é®ç½©" msgid "Invalid version for IP address" msgstr "無效的 IP ä½å€ç‰ˆæœ¬" msgid "Limit Summary" msgstr "é™åˆ¶æ‘˜è¦" msgid "Log in" msgstr "登入" msgid "Login" msgstr "登入" msgid "Members" msgstr "æˆå“¡" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "è¨Šæ¯ JSON 檔 '%(path)s' 的格å¼ä¸æ­£ç¢ºã€‚%(exception)s" msgid "More Actions" msgstr "更多動作" msgid "Navigation Item" msgstr "導覽項目" msgid "Never" msgstr "æ°¸ä¸" msgid "Next" msgstr "下一é " msgid "Next »" msgstr "下一頠»" msgid "No items to display." msgstr "沒有項目å¯ä»¥åˆ—出。" #, python-format msgid "No match returned for the id \"%s\"." msgstr "沒有符åˆè­˜åˆ¥è™Ÿã€Œ%sã€çš„çµæžœã€‚" msgid "No members." msgstr "ç„¡æˆå“¡ã€‚" msgid "None available." msgstr "ç„¡å¯ç”¨çš„。" msgid "Not a valid IP protocol number" msgstr "䏿˜¯æœ‰æ•ˆçš„ IP å”定號碼" msgid "Not a valid port number" msgstr "䏿˜¯æœ‰æ•ˆçš„埠å£è™Ÿç¢¼" msgid "One colon allowed in port range" msgstr "å…許以冒號隔開埠å£ç¯„åœ" msgid "Other" msgstr "其它" msgid "Password is not accepted" msgstr "ä¸èªå¯çš„密碼" msgid "Please log in to continue." msgstr "請登入以繼續。" msgid "Please select a row before taking that action." msgstr "執行動作å‰è«‹é¸æ“‡ä¸€åˆ—。" msgid "Processing..." msgstr "處ç†ä¸­â€¦â€¦" msgid "Save" msgstr "儲存" #, python-format msgid "Select a %s to browse." msgstr "鏿“‡ %s 來ç€è¦½ã€‚" msgid "Select a period of time to query its usage:" msgstr "鏿“‡æ™‚段查詢使用é‡ï¼š" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "販賣å°ç‹—" msgid "Sign In" msgstr "登入" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "已賣出å°ç‹—" msgid "Submit" msgstr "æäº¤" msgid "Success: " msgstr "æˆåŠŸï¼š" msgid "Summary" msgstr "摘è¦" #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "%(obj)s 上ä¸å­˜åœ¨ %(attr)s 屬性。" msgid "The date should be in YYYY-mm-dd format." msgstr "æ—¥æœŸçš„æ ¼å¼æ‡‰è©²ç‚º YY-mm-dd。" msgid "The string may only contain ASCII printable characters." msgstr "字串åªèƒ½åŒ…å« ASCII å¯å°å‡ºçš„字元。" #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "%(resource)s 的值是範本內的 %(name)s。從此介é¢å•Ÿå‹•堆疊時,該值的開頭必須是 " "\"http://\" 或 \"https://\"" msgid "This Period's GB-Hours:" msgstr "此時段的硬碟 GB 時數:" msgid "This Period's RAM-Hours:" msgstr "此時段的虛擬記憶體時數:" msgid "This Period's VCPU-Hours:" msgstr "此時段的虛擬處ç†å™¨æ™‚數:" msgid "This action cannot be undone." msgstr "這個動作將無法回復。" #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "無法%(action)s:%(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "æœªæŽˆæ¬Šã€‚è«‹è©¦è‘—é‡æ–°ç™»å…¥ã€‚" #, python-format msgid "Unauthorized: %s" msgstr "未授權:%s" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "Up Item" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "æ›´æ–°é …ç›®" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "已更新的項目" #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "Upped Item" msgid "Usage Summary" msgstr "ä½¿ç”¨é‡æ‘˜è¦" msgid "Warning: " msgstr "警告:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "ä¸å…許您%(action)s:%(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "您沒有足夠的權é™å­˜å– %s" msgid "You do not have permission to access the resource:" msgstr "您沒有權é™å­˜å–資æºï¼š" horizon-9.0.0/horizon/locale/zh_TW/LC_MESSAGES/djangojs.po0000664000567000056710000001767512701407071024276 0ustar jenkinsjenkins00000000000000# Zhang Xiaowei , 2015. #zanata # Ching Kuo , 2016. #zanata # Jennifer , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-22 09:59+0000\n" "Last-Translator: Jennifer \n" "Language-Team: Chinese (Taiwan)\n" "Language: zh-TW\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s ä½å…ƒçµ„" msgid "(Modified)" msgstr "(已更改)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "使用中" msgid "Add Interface" msgstr "加入網路å¡" msgid "Added" msgstr "已加入" msgid "Allocated" msgstr "已分é…" msgid "An error occurred while updating." msgstr "更新時發生錯誤。" msgid "An error occurred. Please try again later." msgstr "發生錯誤。請ç¨å¾Œå†è©¦ã€‚" msgid "Available" msgstr "å¯ç”¨" msgid "Available Metadata" msgstr "å¯ç”¨çš„詮釋資料" msgid "Back" msgstr "上一步" msgid "Cancel" msgstr "å–æ¶ˆ" msgid "Click here for filters." msgstr "按此檢視篩é¸ã€‚" msgid "Click here to expand the row and view the errors." msgstr "展開這行來檢視錯誤。" msgid "Click to see more details" msgstr "點擊來顯示更多詳細資訊" msgid "Click to show or hide" msgstr "點擊來顯示或隱è—" msgid "Closed" msgstr "已關閉" msgid "Closing" msgstr "關閉中" #, python-format msgid "Confirm %s" msgstr "ç¢ºèª %s" msgid "Confirm Delete Foobars" msgstr "確èªåˆªé™¤ Foobar" msgid "Connecting" msgstr "連線中" msgid "Could not decrypt the password" msgstr "ä¸èƒ½è§£éŽ–å¯†ç¢¼" msgid "Could not read the file" msgstr "ä¸èƒ½è®€å–檔案" msgid "Create Subnet" msgstr "新增å­ç¶²è·¯" msgid "Current Usage" msgstr "ç›®å‰ä½¿ç”¨é‡" msgid "Custom" msgstr "自訂" msgid "Customization Script" msgstr "客製化腳本" msgid "Danger" msgstr "å±éšª" msgid "Danger: " msgstr "å±éšªï¼š" msgid "Decimal required" msgstr "需è¦å進ä½" msgid "Delete" msgstr "刪除" msgid "Delete Instance" msgstr "刪除雲實例" msgid "Delete Interface" msgstr "刪除網路å¡" msgid "Delete Network" msgstr "刪除網路" msgid "Delete Router" msgstr "刪除路由器" msgid "Delete Subnet" msgstr "刪除å­ç¶²è·¯" #, python-format msgid "Deleted : %s." msgstr "已刪除:%s。" msgid "Detail Information" msgstr "詳細的資訊" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "顯示 %(count)s 個項目,總共 %(total)s 個項目" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "顯示 %s é …" msgid "Duplicate keys are not allowed" msgstr "ä¸å…許é‡è¤‡çš„éµå€¼" msgid "Error" msgstr "錯誤" msgid "Error: " msgstr "錯誤:" msgid "Example" msgstr "範例" msgid "Existing Metadata" msgstr "已存在的詮釋資料" msgid "Expand to see allocated items" msgstr "展開來顯示已分é…的項目" msgid "Expand to see available items" msgstr "展開來顯示å¯ç”¨çš„é …ç›®" msgid "Filter" msgstr "篩é¸" msgid "Finish" msgstr "完æˆ" msgid "Flavor" msgstr "虛擬硬體樣æ¿" msgid "Full Text Search" msgstr "全文字æœç´¢" msgid "ID" msgstr "識別號" msgid "IP Addresses" msgstr "IP ä½å€" msgid "Info" msgstr "資訊" msgid "Integer required" msgstr "éœ€è¦æ•´æ•¸" msgid "Interfaces" msgstr "網路å¡" msgid "Load script from a file" msgstr "從檔案載入腳本" msgid "Loading" msgstr "讀å–中" msgid "Max" msgstr "最大" msgid "Max length" msgstr "最大長度" msgid "Min" msgstr "最å°" msgid "Min length" msgstr "最å°é•·åº¦" msgid "Name" msgstr "å稱" msgid "Next" msgstr "下一步" msgid "No" msgstr "å¦" msgid "No Limit" msgstr "ç„¡é™åˆ¶" msgid "No available items" msgstr "ç„¡å¯ç”¨çš„é …ç›®" msgid "No available metadata" msgstr "ç„¡å¯ç”¨çš„詮釋資料" msgid "No data available." msgstr "ç„¡å¯ç”¨çš„資料。" msgid "No description available." msgstr "ç„¡å¯ç”¨çš„æè¿°ã€‚" msgid "No existing metadata" msgstr "ä¸å­˜åœ¨è©®é‡‹è³‡æ–™" msgid "No items to display." msgstr "沒有項目å¯ä»¥åˆ—出。" msgid "No roles" msgstr "無角色" msgid "None" msgstr "ç„¡" msgid "Not authorized to do this operation." msgstr "沒有足夠的權é™åŸ·è¡Œé€™å€‹æ“作。" msgid "Notice: " msgstr "注æ„:" msgid "Open" msgstr "開啟" msgid "Open Console" msgstr "開啟主控臺" msgid "Passwords do not match." msgstr "密碼ä¸ä¸€æ¨£ã€‚" msgid "Pattern mismatch" msgstr "ä¸ç¬¦åˆçš„æ¨£å¼" msgid "Please confirm your selection. " msgstr "è«‹ç¢ºèªæ‚¨çš„鏿“‡ã€‚" msgid "Prompt" msgstr "æç¤º" msgid "Re-order items using drag and drop" msgstr "ä½¿ç”¨æ‹–æ‹‰ä¾†é‡æ–°æŽ’列" msgid "Remaining" msgstr "剩餘的" msgid "Remove" msgstr "移除" msgid "Required" msgstr "需è¦" msgid "Roles" msgstr "角色" msgid "STATUS" msgstr "狀態" #. Strings between {$ and $} should be left untranslated. msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgstr "" "Script 大å°ï¼š{$ (scriptLength || 0) | bytes $},共 {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgid "Search in current results" msgstr "在目å‰çµæžœä¸­æœå°‹" msgid "Select an item from Available items below" msgstr "從下列å¯ç”¨çš„é …ç›®ä¸­é¸æ“‡" msgid "Select one" msgstr "擇一" msgid "Server Name" msgstr "伺æœå™¨å稱" msgid "Shutdown" msgstr "關機中" msgid "Status" msgstr "狀態" #, python-format msgid "Status: %s" msgstr "狀態:%s" msgid "Submit" msgstr "æäº¤" msgid "Subnets" msgstr "å­ç¶²è·¯" msgid "Success" msgstr "æˆåŠŸ" msgid "Success: " msgstr "æˆåŠŸï¼š" msgid "Text" msgstr "文字" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "無法執行動作。這行的內容有錯或者缺少資訊。" msgid "The script is larger than the maximum size" msgstr "該 Script 的大å°å¤§æ–¼å¤§å°ä¸Šé™" msgid "There was a problem communicating with the server, please try again." msgstr "與伺æœå™¨é–“的通訊有å•題,請å†è©¦ä¸€æ¬¡ã€‚" msgid "There was an error submitting the form. Please try again." msgstr "æäº¤è¡¨å–®æ™‚有錯誤。請å†è©¦ä¸€æ¬¡ã€‚" msgid "Toggle Dropdown" msgstr "切æ›ä¸‹æ‹‰å¼é¸å–®" msgid "Toggle navigation" msgstr "切æ›å°Žè¦½" msgid "Total" msgstr "總共" #, python-format msgid "Unable to delete: %s." msgstr "無法刪除:%s。" msgid "Unlimited" msgstr "ç„¡é™" msgid "View Details" msgstr "檢視詳細資訊" msgid "View Instance Details" msgstr "檢視雲實例詳細資訊" msgid "View Router Details" msgstr "檢視路由器詳細資訊" msgid "Warning" msgstr "警告" msgid "Warning: " msgstr "警告:" msgid "Working" msgstr "é‹ä½œä¸­" msgid "Yes" msgstr "是" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "您å¯ä»¥é€éŽå°‡é …ç›®å¾žå·¦æ¬„ç§»å‹•åˆ°å³æ¬„ä¾†æŒ‡å®šè³‡æº meta 資料。左欄包å«ä¾†è‡ª Glance " "meta 資料類別的 meta 資料定義。使用「自訂ã€é¸é …ä¾†æ–°å¢žåŒ…å«æ‰€é¸ç´¢å¼•éµçš„ meta 資" "料。" #, python-format msgid "You have selected %s. " msgstr "æ‚¨é¸æ“‡äº† %s。" msgid "description" msgstr "æè¿°" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "倉庫的é è¨­å€¼" #, python-format msgid "selected \"%s\"" msgstr "å·²é¸å– \"%s\"" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || 'No items to display.' $}" horizon-9.0.0/horizon/locale/ko_KR/0000775000567000056710000000000012701407231020302 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/ko_KR/LC_MESSAGES/0000775000567000056710000000000012701407231022067 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/ko_KR/LC_MESSAGES/django.po0000664000567000056710000002317612701407071023704 0ustar jenkinsjenkins00000000000000# Sungjin Kang , 2015. #zanata # Sungjin Kang , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-18 02:20+0000\n" "Last-Translator: Ian Y. Choi \n" "Language-Team: Korean (South Korea)\n" "Language: ko-KR\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " 다른 사용ìžë¡œ 로그ì¸ì„ 하거나 홈페ì´" "ì§€ 로 ëŒì•„가십시오.\n" " " #, python-format msgid "" "\n" " Used %(used)s (No Limit)\n" " " msgstr "" "\n" " %(used)s (제한 ì—†ìŒ) 사용ë¨\n" " " #, python-format msgid "" "\n" " Used %(used)s of %(available)s \n" " " msgstr "" "\n" " %(available)s ì—서 %(used)s 사용" "ë¨\n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" " ì¸ì¦ ë°©ë²•ì„ ëª¨ë¥´ëŠ”ê²½ìš°, 관리ìžì—게 문ì˜í•˜ì‹­ì‹œì˜¤.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(data_type)s %(action)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(data_type)s %(action)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d ë°”ì´íЏ" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%sê°€ 성공ì ìœ¼ë¡œ 완료ë˜ì—ˆìŠµë‹ˆë‹¤." #, python-format msgid "%s did not complete." msgstr "%sê°€ 완료ë˜ì§€ 않았습니다." msgid "« Prev" msgstr "« ì´ì „" msgid "(No Limit)" msgstr "(제한 ì—†ìŒ)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 Bytes" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "%(resource)s ì— ì´ë¯¸ \"%(name)s\" ì´ë¦„ì´ ì¡´ìž¬í•©ë‹ˆë‹¤." msgid "Actions" msgstr "작업" msgid "Active Instances:" msgstr "ë™ìž‘ ì¤‘ì¸ ì¸ìŠ¤í„´ìŠ¤:" msgid "Active RAM:" msgstr "사용 ì¤‘ì¸ RAM:" msgid "Add a row" msgstr "ì—´ 추가" msgid "All available" msgstr "ëª¨ë‘ ì‚¬ìš©ê°€ëŠ¥" msgid "Available" msgstr "사용 가능" msgid "Back" msgstr "뒤로" #. Translators: test code, don't really have to translate msgid "Batch Item" msgid_plural "Batch Items" msgstr[0] "항목 ì¼ê´„ 처리" #. Translators: test code, don't really have to translate msgid "Batched Item" msgid_plural "Batched Items" msgstr[0] "항목 ì¼ê´„ 처리함" msgid "Cancel" msgstr "취소" msgid "Connect" msgstr "ì—°ê²°" msgid "Delete" msgstr "ì‚­ì œ" msgid "Deleted" msgstr "ì‚­ì œë¨" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "%(content_items)s 항목 표시" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "%(counter)s 항목 표시" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "%(nav_items)s 항목 표시" #. Translators: test code, don't really have to translate msgid "Down Item" msgid_plural "Down Items" msgstr[0] "항목 아래로" #. Translators: test code, don't really have to translate msgid "Downed Item" msgid_plural "Downed Items" msgstr[0] "항목 아래로 옮김" #, python-format msgid "Error processing message json file '%(path)s': %(exception)s" msgstr "메시지 json íŒŒì¼ '%(path)s' 처리 오류: %(exception)s" msgid "Error: " msgstr "오류:" msgid "Fake" msgstr "페ì´í¬" msgid "Filter" msgstr "í•„í„°" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "IP 주소 형ì‹ì´ 잘못 ë˜ì—ˆìŠµë‹ˆë‹¤." msgid "Info: " msgstr "ì •ë³´:" msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "" "올바르지 ì•Šì€ ë©”íƒ€ë°ì´í„° 항목. 콤마로 êµ¬ë¶„ëœ key=value 페어를 사용하세요." msgid "Invalid subnet mask" msgstr "서브넷 마스í¬ê°€ 잘못ë˜ì—ˆìŠµë‹ˆë‹¤." msgid "Invalid version for IP address" msgstr "IP 주소 ë²„ì „ì´ ìž˜ëª»ë˜ì—ˆìŠµë‹ˆë‹¤." msgid "Limit Summary" msgstr "간략한 요약" msgid "Log in" msgstr "로그ì¸" msgid "Login" msgstr "로그ì¸" msgid "Members" msgstr "구성ì›" #, python-format msgid "Message json file '%(path)s' is malformed. %(exception)s" msgstr "메시지 json íŒŒì¼ '%(path)s' ì´ ì†ìƒë˜ì—ˆìŠµë‹ˆë‹¤. %(exception)s" msgid "More Actions" msgstr "기타 작업" msgid "Navigation Item" msgstr "네비게ì´ì…˜ 항목" msgid "Never" msgstr "ì—†ìŒ" msgid "Next" msgstr "다ìŒ" msgid "Next »" msgstr "다쌠»" msgid "No items to display." msgstr "표시할 í•­ëª©ì´ ì—†ìŠµë‹ˆë‹¤." #, python-format msgid "No match returned for the id \"%s\"." msgstr "ID \"%s\" ì— ì¼ì¹˜ë˜ëŠ” í•­ëª©ì´ ì—†ìŠµë‹ˆë‹¤." msgid "No members." msgstr "구성ì›ì´ 없습니다." msgid "None available." msgstr "사용할 수 있는 ê²ƒì´ ì—†ìŠµë‹ˆë‹¤." msgid "Not a valid IP protocol number" msgstr "올바르지 ì•Šì€ IP 프로토콜 번호" msgid "Not a valid port number" msgstr "올바르지 ì•Šì€ í¬íЏ 번호" msgid "One colon allowed in port range" msgstr "í¬íЏ 범위ì—서 ì½œë¡ ì€ í•˜ë‚˜ë§Œ 사용할 수 있습니다." msgid "Other" msgstr "기타" msgid "Password is not accepted" msgstr "허용ë˜ì§€ 않는 비밀번호입니다." msgid "Please log in to continue." msgstr "ê³„ì† ì§„í–‰í•˜ë ¤ë©´ 로그ì¸í•˜ì‹­ì‹œì˜¤." msgid "Please select a row before taking that action." msgstr "해당 ìž‘ì—…ì„ ì‹¤í–‰í•˜ê¸° ì „ì— ì—´(row)ì„ ì„ íƒí•˜ì‹­ì‹œì˜¤." msgid "Processing..." msgstr "작업 중..." msgid "Save" msgstr "저장" #, python-format msgid "Select a %s to browse." msgstr "브ë¼ìš°ì €ì—서 %s 를 ì„ íƒí•˜ì‹­ì‹œì˜¤." msgid "Select a period of time to query its usage:" msgstr "ì‚¬ìš©ëŸ‰ì„ ì¡°íšŒí•  ê¸°ê°„ì„ ì„ íƒí•˜ì„¸ìš”:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "ê°•ì•„ì§€ íŒë§¤" msgid "Sign In" msgstr "로그ì¸" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "ê°•ì•„ì§€ íŒë§¤í•¨" msgid "Submit" msgstr "제출" msgid "Success: " msgstr "완료:" msgid "Summary" msgstr "요약" #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." msgstr "%(attr)s ì†ì„±ì´ %(obj)s ì— ì¡´ìž¬í•˜ì§€ 않습니다." msgid "The date should be in YYYY-mm-dd format." msgstr "날짜는 YYYY-mm-dd 형ì‹ì´ì–´ì•¼ 합니다." msgid "The string may only contain ASCII printable characters." msgstr "문ìžì—´ì€ ASCII ì¸ì‡„ 문ìžë¥¼ í¬í•¨í•  수 있습니다." #, python-format msgid "" "The value of %(resource)s is %(name)s inside the template. When launching a " "stack from this interface, the value must start with \"http://\" or " "\"https://\"" msgstr "" "%(resource)s ê°’ì€ í…œí”Œë¦¿ ë‚´ %(name)s 입니다. 해당 ì¸í„°íŽ˜ì´ìŠ¤ë¡œë¶€í„° stackì„ êµ¬" "ë™í•˜ëŠ” 경우, 해당 ê°’ì€ \"http://\" ë˜ëŠ” \"https://\" 로 시작해야 합니다" msgid "This Period's GB-Hours:" msgstr "ì„ íƒ ê¸°ê°„ ë™ì•ˆì— 사용 시간당 GB:" msgid "This Period's RAM-Hours:" msgstr "ì„ íƒ ê¸°ë‹¨ ë™ì•ˆì— 사용 시간당 RAM:" msgid "This Period's VCPU-Hours:" msgstr "ì„ íƒ ê¸°ê°„ ë™ì•ˆì— 사용 시간당 VCPU:" msgid "This action cannot be undone." msgstr "ì´ ìž‘ì—…ì€ ì·¨ì†Œí•  수 없습니다." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "%(action)sì„(를) í•  수 없습니다.: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤. 다시 ë¡œê·¸ì¸ í•´ì£¼ì‹­ì‹œì˜¤." #, python-format msgid "Unauthorized: %s" msgstr "ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤: %s" #. Translators: test code, don't really have to translate msgid "Up Item" msgid_plural "Up Items" msgstr[0] "항목 위로" msgid "Update Item" msgid_plural "Update Items" msgstr[0] "항목 ì—…ë°ì´íЏ" msgid "Updated Item" msgid_plural "Updated Items" msgstr[0] "항목 ì—…ë°ì´íЏë¨" #. Translators: test code, don't really have to translate msgid "Upped Item" msgid_plural "Upped Items" msgstr[0] "항목 위로 옮김" msgid "Usage Summary" msgstr "사용량 요약" msgid "Warning: " msgstr "경고:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "%(action)s ì´(ê°€) 허용ë˜ì§€ 않았습니다: %(objs)s " #, python-format msgid "You are not authorized to access %s" msgstr "%s ì— ì ‘ê·¼ ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤. " msgid "You do not have permission to access the resource:" msgstr "ë‹¹ì‹ ì€ ë¦¬ì†ŒìŠ¤ì— ì ‘ê·¼í•  ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤.:" horizon-9.0.0/horizon/locale/ko_KR/LC_MESSAGES/djangojs.po0000664000567000056710000002131612701407071024233 0ustar jenkinsjenkins00000000000000# Sungjin Kang , 2015. #zanata # Ian Y. Choi , 2016. #zanata # Sungjin Kang , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-18 21:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-20 02:32+0000\n" "Last-Translator: Ian Y. Choi \n" "Language-Team: Korean (South Korea)\n" "Language: ko-KR\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s ë°”ì´íЏ" msgid "(Modified)" msgstr "(수정ë¨)" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "Active" msgid "Add Interface" msgstr "ì¸í„°íŽ˜ì´ìФ 추가" msgid "Added" msgstr "추가ë¨" msgid "Allocated" msgstr "할당ë¨" msgid "An error occurred while updating." msgstr "ì—…ë°ì´íЏ 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤." msgid "An error occurred. Please try again later." msgstr "오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. ë‚˜ì¤‘ì— ë‹¤ì‹œ 시ë„하십시오." msgid "Available" msgstr "사용 가능" msgid "Available Metadata" msgstr "사용 가능한 메타ë°ì´í„°" msgid "Back" msgstr "뒤로" msgid "Cancel" msgstr "취소" msgid "Click here for filters." msgstr "필터를 ì„ íƒí•˜ì‹­ì‹œì˜¤." msgid "Click here to expand the row and view the errors." msgstr "í´ë¦­ìœ¼ë¡œ ì—´ì„ í™•ìž¥í•˜ì—¬ ì—러를 확ì¸í•  수 있습니다." msgid "Click to see more details" msgstr "í´ë¦­í•˜ì—¬ 세부 사항 보기" msgid "Click to show or hide" msgstr "í´ë¦­í•˜ì—¬ 보기 ë˜ëŠ” ê°ì¶”기" msgid "Closed" msgstr "ë‹«ìŒ" msgid "Closing" msgstr "닫는중" #, python-format msgid "Confirm %s" msgstr "%s를 확ì¸í•˜ì„¸ìš”." msgid "Confirm Delete Foobars" msgstr "Foobar ì‚­ì œ 확ì¸" msgid "Connecting" msgstr "연결중" msgid "Could not decrypt the password" msgstr "암호를 í•´ë…í•  수 없습니다." msgid "Could not read the file" msgstr "파ì¼ì„ ì½ì„ 수 없습니다" msgid "Create Subnet" msgstr "서브넷 ìƒì„±" msgid "Current Usage" msgstr "현재 사용량" msgid "Custom" msgstr "ì‚¬ìš©ìž ì§€ì •" msgid "Customization Script" msgstr "ì‚¬ìš©ìž ì •ì˜ ìŠ¤í¬ë¦½íЏ" msgid "Danger" msgstr "위험" msgid "Danger: " msgstr "위험:" msgid "Decimal required" msgstr "실수 í•„ìš”" msgid "Delete" msgstr "ì‚­ì œ" msgid "Delete Instance" msgstr "ì¸ìŠ¤í„´ìŠ¤ ì‚­ì œ" msgid "Delete Interface" msgstr "ì¸í„°íŽ˜ì´ìФ ì‚­ì œ" msgid "Delete Network" msgstr "ë„¤íŠ¸ì›Œí¬ ì‚­ì œ" msgid "Delete Router" msgstr "ë¼ìš°í„° ì‚­ì œ" msgid "Delete Subnet" msgstr "서브넷 ì‚­ì œ" #, python-format msgid "Deleted : %s." msgstr "ì‚­ì œë¨ : %s." msgid "Detail Information" msgstr "ìƒì„¸ ì •ë³´" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "%(total)s í•­ëª©ì— ëŒ€í•œ %(count)s 표시중" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "%s 항목 표시" msgid "Duplicate keys are not allowed" msgstr "중복 키를 허용하지 않습니다." msgid "Error" msgstr "Error" msgid "Error: " msgstr "오류:" msgid "Example" msgstr "예제" msgid "Existing Metadata" msgstr "기존 메타ë°ì´í„°" msgid "Expand to see allocated items" msgstr "í• ë‹¹ëœ í•­ëª©ì„ ë³¼ 수 있ë„ë¡ í™•ìž¥" msgid "Expand to see available items" msgstr "사용 가능한 í•­ëª©ì„ ë³¼ 수 있ë„ë¡ í™•ìž¥" msgid "Filter" msgstr "í•„í„°" msgid "Finish" msgstr "완료" msgid "Flavor" msgstr "Flavor" msgid "Full Text Search" msgstr "모든 í…스트 검색" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "IP 주소" msgid "Info" msgstr "ì •ë³´" msgid "Integer required" msgstr "정수 í•„ìš”" msgid "Interfaces" msgstr "ì¸í„°íŽ˜ì´ìФ" msgid "Load script from a file" msgstr "파ì¼ì—서 스í¬ë¦½íЏ ì½ì–´ì˜¤ê¸°" msgid "Loading" msgstr "불러오는 중" msgid "Max" msgstr "최대" msgid "Max length" msgstr "최대 길ì´" msgid "Min" msgstr "최소" msgid "Min length" msgstr "최소 길ì´" msgid "Name" msgstr "ì´ë¦„" msgid "Next" msgstr "다ìŒ" msgid "No" msgstr "아니오" msgid "No Limit" msgstr "제한 ì—†ìŒ" msgid "No available items" msgstr "사용 가능한 항목 ì—†ìŒ" msgid "No available metadata" msgstr "사용 가능한 메타ë°ì´í„°ê°€ 없습니다." msgid "No data available." msgstr "ë°ì´í„°ê°€ 없습니다." msgid "No description available." msgstr "사용 가능한 ì„¤ëª…ì´ ì—†ìŠµë‹ˆë‹¤." msgid "No existing metadata" msgstr "기존 메타ë°ì´í„°ê°€ 없습니다." msgid "No items to display." msgstr "표시할 í•­ëª©ì´ ì—†ìŠµë‹ˆë‹¤." msgid "No roles" msgstr "ì—­í•  ì—†ìŒ" msgid "None" msgstr "None" msgid "Not authorized to do this operation." msgstr "ì´ ìž‘ì—…ì— ëŒ€í•œ ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤." msgid "Notice: " msgstr "주ì˜:" msgid "Open" msgstr "열림" msgid "Open Console" msgstr "콘솔 열기" msgid "Passwords do not match." msgstr "비밀번호가 ì¼ì¹˜í•˜ì§€ 않습니다." msgid "Pattern mismatch" msgstr "패턴 불ì¼ì¹˜" msgid "Please confirm your selection. " msgstr "ì„ íƒ ì‚¬í•­ì„ í™•ì¸í•˜ì‹­ì‹œì˜¤." msgid "Prompt" msgstr "프롬프트" msgid "Re-order items using drag and drop" msgstr "다시 주문할 ì•„ì´í…œì€ 드래그 앤 드롭으로 가져옵니다." msgid "Remaining" msgstr "기억하기" msgid "Remove" msgstr "ì‚­ì œ" msgid "Required" msgstr "í•„ìš”" msgid "Roles" msgstr "ì—­í• " msgid "STATUS" msgstr "STATUS" #. Strings between {$ and $} should be left untranslated. msgid "" "Script size: {$ (scriptLength || 0) | bytes $} of {$ config.MAX_SCRIPT_SIZE " "| bytes $}" msgstr "" "스í¬ë¦½íЏ í¬ê¸°: {$ config.MAX_SCRIPT_SIZE | bytes $} 중 {$ (scriptLength || " "0) | bytes $}" msgid "Search in current results" msgstr "현재 ê²°ê³¼ì—서 검색" msgid "Select an item from Available items below" msgstr "ì•„ëž˜ì— ì‚¬ìš© 가능한 항목ì—서 ì„ íƒ" msgid "Select one" msgstr "하나 ì„ íƒ" msgid "Server Name" msgstr "서버 ì´ë¦„" msgid "Shutdown" msgstr "Shutdown" msgid "Status" msgstr "ìƒíƒœ" #, python-format msgid "Status: %s" msgstr "ìƒíƒœ: %s" msgid "Submit" msgstr "제출" msgid "Subnets" msgstr "서브넷" msgid "Success" msgstr "완료" msgid "Success: " msgstr "완료:" msgid "Text" msgstr "í…스트" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "ìž‘ì—…ì„ ì§„í–‰í•  수 없습니다. ì´ í–‰ ë‚´ìš©ì— ì˜¤ë¥˜ê°€ 있거나 ì •ë³´ê°€ 누ë½ë˜ì—ˆìŠµë‹ˆë‹¤." msgid "The script is larger than the maximum size" msgstr "스í¬ë¦½íŠ¸ê°€ 최대 í¬ê¸°ë³´ë‹¤ í½ë‹ˆë‹¤" msgid "There was a problem communicating with the server, please try again." msgstr "ì„œë²„ì™€ì˜ í†µì‹ ì— ë¬¸ì œê°€ ë°œìƒí•˜ì˜€ìœ¼ë‹ˆ, 다시 시ë„하세요." msgid "There was an error submitting the form. Please try again." msgstr "ì–‘ì‹ì„ 제출하는 ë™ì•ˆ 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤. 다시 시ë„하세요." msgid "Toggle Dropdown" msgstr "토글 드롭다운" msgid "Toggle navigation" msgstr "내비게ì´ì…˜ 토글" msgid "Total" msgstr "ì´í•©" #, python-format msgid "Unable to delete: %s." msgstr "삭제할 수 없습니다: %s." msgid "Unlimited" msgstr "제한없ìŒ" msgid "View Details" msgstr "세부 ì •ë³´ 보기" msgid "View Instance Details" msgstr "ì¸ìŠ¤í„´ìŠ¤ ì •ë³´ ìžì„¸ížˆ 보기" msgid "View Router Details" msgstr "ë¼ìš°í„° ì •ë³´ ìžì„¸ížˆ 보기" msgid "Warning" msgstr "주ì˜" msgid "Warning: " msgstr "경고:" msgid "Working" msgstr "작업 중" msgid "Yes" msgstr "예" msgid "" "You can specify resource metadata by moving items from the left column to " "the right column. In the left column there are metadata definitions from the " "Glance Metadata Catalog. Use the \"Custom\" option to add metadata with " "the key of your choice." msgstr "" "오른쪽 ì—´ì—서 왼쪽 열로 ì´ë™í•˜ì—¬ 리소스 메타ë°ì´í„°ë¥¼ 지정할 수 있습니다. 왼" "쪽 ì—´ì—는 Glance 메타ë°ì´í„° ì¹´íƒˆë¡œê·¸ë¡œë¶€í„°ì˜ ë©”íƒ€ë°ì´í„° ì •ì˜ê°€ 존재합니다. " "\"ì‚¬ìš©ìž ì •ì˜\" ì˜µì…˜ì„ ì‚¬ìš©í•˜ì—¬ ì„ íƒí•œ í‚¤ì— ë©”íƒ€ë°ì´í„°ë¥¼ 추가할 수 있습니다." #, python-format msgid "You have selected %s. " msgstr "%s를 ì„ íƒí•˜ì˜€ìŠµë‹ˆë‹¤. " msgid "description" msgstr "설명" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "Pool 기본" #, python-format msgid "selected \"%s\"" msgstr "ì„ íƒëœ \"%s\"" msgid "{$ message || 'No items to display.' $}" msgstr "{$ message || '표시할 í•­ëª©ì´ ì—†ìŠµë‹ˆë‹¤.' $}" horizon-9.0.0/horizon/locale/pl_PL/0000775000567000056710000000000012701407231020303 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/pl_PL/LC_MESSAGES/0000775000567000056710000000000012701407231022070 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/pl_PL/LC_MESSAGES/django.po0000664000567000056710000001645512701407063023710 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata # Åukasz Jernas , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2015-09-21 10:56+0000\n" "Last-Translator: Åukasz Jernas \n" "Language-Team: Polish (Poland)\n" "Language: pl-PL\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2)\n" #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" msgid "" "\n" " If you are not sure which authentication method to use, contact your " "administrator.\n" " " msgstr "" "\n" "JeÅ›li nie wiadomo, którego sposobu uwierzytelniania użyć, należy " "skontaktować siÄ™ z administratorem.\n" " " #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d bajt" msgstr[1] "%(size)d bajty" msgstr[2] "%(size)d bajtów" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s zakoÅ„czona pomyÅ›lnie." #, python-format msgid "%s did not complete." msgstr "%s nie zostaÅ‚a zakoÅ„czona." msgid "« Prev" msgstr "« Poprzednie" msgid "(No Limit)" msgstr "(Bez ograniczeÅ„)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 bajtów" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "%(resource)s o nazwie \"%(name)s\" już istnieje." msgid "Actions" msgstr "CzynnoÅ›ci" msgid "Active Instances:" msgstr "Aktywne instancje:" msgid "Active RAM:" msgstr "Aktywna pamięć RAM:" msgid "Add a row" msgstr "Dodaj wiersz" msgid "All available" msgstr "CaÅ‚ość dostÄ™pna" msgid "Available" msgstr "DostÄ™pne" msgid "Back" msgstr "Wstecz" msgid "Cancel" msgstr "Anuluj" msgid "Connect" msgstr "Połącz" msgid "Delete" msgstr "UsuÅ„" msgid "Deleted" msgstr "UsuniÄ™to" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] "WyÅ›wietlanie %(content_items)s pozycji" msgstr[1] "WyÅ›wietlanie %(content_items)s pozycji" msgstr[2] "WyÅ›wietlanie %(content_items)s pozycji" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] "WyÅ›wietlanie %(counter)s pozycji" msgstr[1] "WyÅ›wietlanie %(counter)s pozycji" msgstr[2] "WyÅ›wietlanie %(counter)s pozycji" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "WyÅ›wietlanie %(nav_items)s pozycjÄ™" msgstr[1] "WyÅ›wietlanie %(nav_items)s pozycji" msgstr[2] "WyÅ›wietlanie %(nav_items)s pozycji" msgid "Error: " msgstr "Błąd:" msgid "Fake" msgstr "FaÅ‚szywe" msgid "Filter" msgstr "Filtr" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Błędny format adresu IP" msgid "Info: " msgstr "Informacja:" msgid "Invalid metadata entry. Use comma-separated key=value pairs" msgstr "" "Błędny wpis metadanych. Należy korzystać z rozdzielonej przecinkami pary " "klucz=wartość ." msgid "Invalid subnet mask" msgstr "Błędna maska podsieci" msgid "Invalid version for IP address" msgstr "Błędna wersja adresu IP" msgid "Limit Summary" msgstr "Podsumowanie limitów" msgid "Login" msgstr "Login" msgid "Members" msgstr "CzÅ‚onkowie" msgid "More Actions" msgstr "PozostaÅ‚e akcje" msgid "Navigation Item" msgstr "Pozycja nawigacyjna" msgid "Never" msgstr "Nigdy" msgid "Next" msgstr "NastÄ™pny" msgid "Next »" msgstr "NastÄ™pne »" msgid "No items to display." msgstr "Brak pozycji do wyÅ›wietlenia." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Nie znaleziono wyników dla identyfikatora „%sâ€." msgid "No members." msgstr "Brak czÅ‚onków." msgid "None available." msgstr "CaÅ‚ość wykorzystana" msgid "Not a valid IP protocol number" msgstr "Błędny numer protokoÅ‚u IP" msgid "Not a valid port number" msgstr "Błędny numer portu" msgid "One colon allowed in port range" msgstr "Tylko jeden dwukropek jest dozwolony w zakresie portów" msgid "Other" msgstr "Inne" msgid "Password is not accepted" msgstr "HasÅ‚o nie zostaÅ‚o zaakceptowane" msgid "Please log in to continue." msgstr "Należy siÄ™ zalogować, aby można byÅ‚o kontynuować." msgid "Please select a row before taking that action." msgstr "Należy wybrać wiersz przed wykonaniem tej czynnoÅ›ci." msgid "Processing..." msgstr "Przetwarzanie…" msgid "Save" msgstr "Zapisz" #, python-format msgid "Select a %s to browse." msgstr "Wybierz %s by przeglÄ…dać." msgid "Select a period of time to query its usage:" msgstr "Wybierz okres za który nalezy wyÅ›wietlić wykorzystanie:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Sprzedaj pieska" msgstr[1] "Sprzedaj pieski" msgstr[2] "Sprzedaj pieski" msgid "Sign In" msgstr "Wpisz siÄ™" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "Sprzedano pieska" msgstr[1] "Sprzedano pieski" msgstr[2] "Sprzedano pieski" msgid "Submit" msgstr "WyÅ›lij" msgid "Success: " msgstr "Powodzenie:" msgid "Summary" msgstr "Podsumowanie" msgid "The date should be in YYYY-mm-dd format." msgstr "Data powinna być podana w formacie YYYY-mm-dd." msgid "The string may only contain ASCII printable characters." msgstr "CiÄ…g może zawierać wyłącznie drukowalne znaki ASCII." msgid "This Period's GB-Hours:" msgstr "GB-godziny w tym okresie:" msgid "This Period's RAM-Hours:" msgstr "RAM-godziny w tym okresie:" msgid "This Period's VCPU-Hours:" msgstr "VCPU-godziny w tym okresie:" msgid "This action cannot be undone." msgstr "Tej czynnoÅ›ci nie można cofnąć." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Nie można %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Brak uprawnieÅ„. ProszÄ™ spróbować siÄ™ wylogować i zalogować ponownie." #, python-format msgid "Unauthorized: %s" msgstr "Nieupoważniono: %s" msgid "Usage Summary" msgstr "Podsumowanie wykorzystania" msgid "Warning: " msgstr "Ostrzeżenie:" #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Brak uprawnieÅ„ do %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "Brak uprawnieÅ„ dostÄ™pu do %s" msgid "You do not have permission to access the resource:" msgstr "Brak uprawnieÅ„ do zasobu:" horizon-9.0.0/horizon/locale/pl_PL/LC_MESSAGES/djangojs.po0000664000567000056710000001514412701407063024237 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata # Åukasz Jernas , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2015-09-21 11:05+0000\n" "Last-Translator: Åukasz Jernas \n" "Language-Team: Polish (Poland)\n" "Language: pl-PL\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s bajtów" msgid "-" msgstr "-" msgid "Active" msgstr "Aktywny" msgid "Add Interface" msgstr "Dodaj interfejs" msgid "Added" msgstr "Dodano" msgid "Allocated" msgstr "Przydzielono" msgid "An error occurred while updating." msgstr "WystÄ…piÅ‚ błąd podczas aktualizacji." msgid "An error occurred. Please try again later." msgstr "WystÄ…piÅ‚ błąd. ProszÄ™ spróbować później." msgid "Available" msgstr "DostÄ™pne" msgid "Available Metadata" msgstr "DostÄ™pne metadane" msgid "Back" msgstr "Wstecz" msgid "Cancel" msgstr "Anuluj" msgid "Click here for filters." msgstr "KlikniÄ™cie tu, spowoduje wyÅ›wietlenie filtrów." msgid "Click here to expand the row and view the errors." msgstr "Kliknij tutaj, aby rozszerzyć wiersz i wyÅ›wietlić błędy." msgid "Click to see more details" msgstr "Kliknij, aby wyÅ›wietlić szczegóły" msgid "Click to show or hide" msgstr "Kliknij by pokazać lub ukryć" msgid "Closed" msgstr "ZamkniÄ™to" msgid "Closing" msgstr "Zamykanie" #, python-format msgid "Confirm %s" msgstr "Potwierdź %s" msgid "Connecting" msgstr "ÅÄ…czenie" msgid "Could not decrypt the password" msgstr "Nie można odszyfrować hasÅ‚a" msgid "Could not read the file" msgstr "Nie można odczytać pliku" msgid "Create Subnet" msgstr "Utwórz podsieć" msgid "Current Usage" msgstr "Bieżące wykorzystanie" msgid "Custom" msgstr "WÅ‚asne" msgid "Danger" msgstr "Zagrożenie" msgid "Danger: " msgstr "Zagrożenie:" msgid "Decimal required" msgstr "Wymagana liczba dziesiÄ™tna" msgid "Delete" msgstr "UsuÅ„" msgid "Delete Interface" msgstr "UsuÅ„ interfejs" msgid "Delete Network" msgstr "UsuÅ„ sieć" msgid "Delete Router" msgstr "UsuÅ„ router" msgid "Delete Subnet" msgstr "UsuÅ„ podsieć" msgid "Detail Information" msgstr "Szczegółowe informacje" #, python-format msgid "Displaying %(count)s of %(total)s items" msgstr "WyÅ›wietlanie %(count)s z %(total)s pozycji" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "WyÅ›wietlanie %s pozycji" msgstr[1] "WyÅ›wietlanie %s pozycji" msgstr[2] "WyÅ›wietlanie %s pozycji" msgid "Duplicate keys are not allowed" msgstr "Ponowne użycie nazwy klucza jest niedozwolone." msgid "Error" msgstr "Błąd" msgid "Error: " msgstr "Błąd:" msgid "Existing Metadata" msgstr "IstniejÄ…ce metadane" msgid "Expand to see allocated items" msgstr "Rozszerz, aby zobaczyć przydzielone pozycje" msgid "Expand to see available items" msgstr "Rozszerz, aby zobaczyć dostepne pozycje" msgid "Filter" msgstr "Filtr" msgid "Finish" msgstr "ZakoÅ„cz" msgid "Flavor" msgstr "Odmiana" msgid "ID" msgstr "ID" msgid "IP Addresses" msgstr "Adresy IP" msgid "Info" msgstr "Info" msgid "Integer required" msgstr "Wymagana liczba caÅ‚kowita" msgid "Interfaces" msgstr "Interfejsy" msgid "Loading" msgstr "Wczytywanie" msgid "Max" msgstr "Maks." msgid "Max length" msgstr "Maks. dÅ‚ugość" msgid "Min" msgstr "Min." msgid "Min length" msgstr "Min. dÅ‚ugość" msgid "Name" msgstr "Nazwa" msgid "Next" msgstr "NastÄ™pny" msgid "No" msgstr "Nie" msgid "No available items" msgstr "Brak dostÄ™pnych pozycji" msgid "No available metadata" msgstr "Metadane niedostÄ™pne" msgid "No data available." msgstr "Brak dostÄ™pnych danych." msgid "No description available." msgstr "Brak dostÄ™pnego opisu." msgid "No existing metadata" msgstr "Brak metadanych" msgid "No items to display." msgstr "Brak pozycji do wyÅ›wietlenia." msgid "No roles" msgstr "Brak ról" msgid "None" msgstr "Brak" msgid "Not authorized to do this operation." msgstr "Nie upoważniono do przeprowadzenie tej czynnoÅ›ci." msgid "Notice: " msgstr "Powiadomienie:" msgid "Open" msgstr "Otwórz" msgid "Open Console" msgstr "Otwórz konsolÄ™" msgid "Passwords do not match." msgstr "HasÅ‚a nie pasujÄ…" msgid "Pattern mismatch" msgstr "Szablon siÄ™ nie zgadza" msgid "Please confirm your selection. " msgstr "Należy potwierdzić wybór." msgid "Prompt" msgstr "Zapytanie" msgid "Re-order items using drag and drop" msgstr "ZmieÅ„ kolejność pozycji poprzez przesuwanie i upuszczanie" msgid "Remaining" msgstr "PozostaÅ‚o" msgid "Remove" msgstr "UsuÅ„" msgid "Required" msgstr "Wymagane" msgid "Roles" msgstr "Role" msgid "STATUS" msgstr "STATUS" msgid "Select an item from Available items below" msgstr "Należy wybrać pozycjÄ™ z listy dostÄ™pnych pozycji poniżej" msgid "Select one" msgstr "Wybierz jedno" msgid "Server Name" msgstr "Nazwa serwera" msgid "Shutdown" msgstr "Wyłącz" msgid "Status" msgstr "Stan" #, python-format msgid "Status: %s" msgstr "Stan: %s" msgid "Submit" msgstr "WyÅ›lij" msgid "Subnets" msgstr "Podsieci" msgid "Success" msgstr "Sukces" msgid "Success: " msgstr "Powodzenie:" msgid "Text" msgstr "Tekst" msgid "" "The action cannot be performed. The contents of this row have errors or are " "missing information." msgstr "" "Nie można wykonać czynnoÅ›ci. Zawartość tego wiersza zawiera błędy lub " "brakujÄ…ce dane." msgid "There was a problem communicating with the server, please try again." msgstr "WystÄ…piÅ‚ problem w komunikacji z serwerem, proszÄ™ spróbować ponownie." msgid "There was an error submitting the form. Please try again." msgstr "WystÄ…piÅ‚ błąd podczas wysyÅ‚ania formularza. ProszÄ™ spróbować ponownie." msgid "Total" msgstr "ÅÄ…cznie" msgid "View Details" msgstr "WyÅ›wietl szczegóły" msgid "View Instance Details" msgstr "WyÅ›wietl szczegóły instancji" msgid "View Router Details" msgstr "WyÅ›wietl szczegóły routera" msgid "Warning" msgstr "Uwaga" msgid "Warning: " msgstr "Ostrzeżenie:" msgid "Working" msgstr "Praca" msgid "Yes" msgstr "Tak" #, python-format msgid "You have selected %s. " msgstr "Wybrano %s." msgid "description" msgstr "opis" msgid "m1.small" msgstr "m1.small" msgid "m1.tiny" msgstr "m1.tiny" msgid "pool default" msgstr "domyÅ›lna pula" horizon-9.0.0/horizon/locale/hi/0000775000567000056710000000000012701407231017675 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/hi/LC_MESSAGES/0000775000567000056710000000000012701407231021462 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/hi/LC_MESSAGES/django.po0000664000567000056710000001775212701407063023303 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Hindi\n" "Language: hi\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%(context_name)s:" msgstr "%(context_name)s:" #, python-format msgid "%(name)s: %(error)s" msgstr "%(name)s: %(error)s" #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d बाइट" msgstr[1] "%(size)d बाइट" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s केबी" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s सफलतापूरà¥à¤µà¤• पूरà¥à¤£." #, python-format msgid "%s did not complete." msgstr "%s पूरà¥à¤£ नहीं है" msgid "« Prev" msgstr "« à¤ªà¤¿à¤›à¤²à¤¾" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 बाइट" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "à¤à¤• %(resource)s , \"%(name)s\" से पहले से ही मौजूद है " msgid "Actions" msgstr "कà¥à¤°à¤¿à¤¯à¤¾à¤à¤‚" msgid "Active Instances:" msgstr "सकà¥à¤°à¤¿à¤¯ इंसà¥à¤Ÿà¥‡à¤‚सेस:" msgid "Active RAM:" msgstr "सकà¥à¤°à¤¿à¤¯ रैम" msgid "Add a row" msgstr "पंकà¥à¤¤à¤¿ जोड़ें" msgid "All available" msgstr "सभी उपलबà¥à¤§" msgid "Available" msgstr "उपलबà¥à¤§" msgid "Back" msgstr "पीछे" msgid "Cancel" msgstr "रदà¥à¤¦ करें" msgid "Delete" msgstr "मिटाà¤à¤" msgid "Deleted" msgstr "मिटाया" #, python-format msgid "Displaying %(content_items)s item" msgid_plural "Displaying %(content_items)s items" msgstr[0] " %(content_items)s मद पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤" msgstr[1] " %(content_items)s मद पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤" #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" msgstr[0] " %(counter)s मद पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤" msgstr[1] " %(counter)s मद पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤" #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" msgstr[0] "%(nav_items)s मद पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤" msgstr[1] "%(nav_items)s मद पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤" msgid "Error: " msgstr "तà¥à¤°à¥à¤Ÿà¤¿: " msgid "Fake" msgstr "जाली" msgid "Filter" msgstr "छानना शà¥à¤°à¥‚..." msgid "Horizon" msgstr "होरिजन" msgid "Incorrect format for IP address" msgstr "IP पता के लिठगलत पà¥à¤°à¤¾à¤°à¥‚प" msgid "Info: " msgstr "सूचना: " msgid "Invalid subnet mask" msgstr "अवैध सबनेट मासà¥à¤•" msgid "Invalid version for IP address" msgstr "IP पता के लिठअवैध पà¥à¤°à¤¾à¤°à¥‚प संसà¥à¤•रण" msgid "Limit Summary" msgstr "सारांश सीमा" msgid "Login" msgstr "लॉगिन" msgid "Members" msgstr "सदसà¥à¤¯" msgid "More Actions" msgstr "अधिक कà¥à¤°à¤¿à¤¯à¤¾" msgid "Navigation Item" msgstr "नेविगेशन आइटम" msgid "Never" msgstr "कभी नहीं" msgid "Next" msgstr "अगला" msgid "Next »" msgstr "अगला »" msgid "No items to display." msgstr "दिखाने के लिठकोई सामगà¥à¤°à¤¿à¤¯à¥‹à¤‚ नहीं." #, python-format msgid "No match returned for the id \"%s\"." msgstr "इस आइडी \"%s\" के लिठकोई मेल नहीं मिला." msgid "No members." msgstr "कोई सदसà¥à¤¯ नहीं" msgid "None available." msgstr "कोई उपलबà¥à¤§ नहीं" msgid "Not a valid IP protocol number" msgstr "वैध IP पà¥à¤°à¥‹à¤Ÿà¥‹à¤•ॉल संखà¥à¤¯à¤¾ नहीं है" msgid "Not a valid port number" msgstr "वैध पोरà¥à¤Ÿ संखà¥à¤¯à¤¾ नहीं है" msgid "One colon allowed in port range" msgstr "पोरà¥à¤Ÿ शà¥à¤°à¥‡à¤£à¥€ में à¤à¤• कॉलन की अनà¥à¤®à¤¤à¤¿ " msgid "Other" msgstr "अनà¥à¤¯" msgid "Password is not accepted" msgstr "पासवरà¥à¤¡ सà¥à¤µà¥€à¤•ार नहीं किया है" msgid "Please log in to continue." msgstr "जारी रखने के लिठमें लॉग इन करें." msgid "Please select a row before taking that action." msgstr "कृपया इस कà¥à¤°à¤¿à¤¯à¤¾ को करने से पहले पंकà¥à¤¤à¤¿ का चयन करें." msgid "Processing..." msgstr "पà¥à¤°à¥‹à¤¸à¥‡à¤¸à¤¿à¤‚ग..." msgid "Save" msgstr "सहेजें" #, python-format msgid "Select a %s to browse." msgstr "à¤à¤•% s को बà¥à¤°à¤¾à¤‰à¤œà¤¼ करने के लिठचयन करें." msgid "Select a period of time to query its usage:" msgstr "इसके उपयोग की पूछताछ करने के लिठसमय की अवधि का चयन करें:" #. Translators: test code, don't really have to translate msgid "Sell Puppy" msgid_plural "Sell Puppies" msgstr[0] "Puppy बेचें " msgstr[1] "Puppies बेचें " msgid "Sign In" msgstr "साइन इन" #. Translators: test code, don't really have to translate msgid "Sold Puppy" msgid_plural "Sold Puppies" msgstr[0] "बेचा हà¥à¤† Puppy" msgstr[1] "बेचा हà¥à¤† Puppy" msgid "Submit" msgstr "जमा करें" msgid "Success: " msgstr "सफलता:" msgid "Summary" msgstr "सारांश" msgid "The date should be in YYYY-mm-dd format." msgstr "तिथि को YYYY-mm-dd पà¥à¤°à¤¾à¤°à¥‚प में होना चाहिà¤" msgid "This Period's GB-Hours:" msgstr "इस अवधि के गीगाबाइट-घंटे" msgid "This Period's RAM-Hours:" msgstr "इस अवधि के रैम-घंटे " msgid "This Period's VCPU-Hours:" msgstr "इस अवधि के VCPU-घंटे" msgid "This action cannot be undone." msgstr "यह कà¥à¤°à¤¿à¤¯à¤¾ पूरà¥à¤µà¤µà¤¤ नहीं किया जा सकता।" #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "%(action)s की अनà¥à¤®à¤¤à¤¿ नहीं है.: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "अनधिकृत. फिर लॉग इन करने का पà¥à¤°à¤¯à¤¾à¤¸ करें." #, python-format msgid "Unauthorized: %s" msgstr "अनधिकृत: %s" msgid "Usage Summary" msgstr "पà¥à¤°à¤¯à¥‹à¤— सार" msgid "Warning: " msgstr "चेतावनी: " #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "आपको %(action)s की अनà¥à¤®à¤¤à¤¿ नहीं है.: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "आप %sका उपयोग करने के लिठअधिकृत नहीं हैं" msgid "You do not have permission to access the resource:" msgstr "संसाधन पर पहà¥à¤à¤š के लिठआपको अनà¥à¤®à¤¤à¤¿ नहीं है:" horizon-9.0.0/horizon/locale/hi/LC_MESSAGES/djangojs.po0000664000567000056710000001072512701407063023631 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Hindi\n" "Language: hi\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s केबी" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" msgid "Active" msgstr "सकà¥à¤°à¤¿à¤¯" msgid "Add Interface" msgstr "अंतरफलक जोड़ें" msgid "Added" msgstr "जोड़ा गया" msgid "An error occurred while updating." msgstr "अदà¥à¤¯à¤¤à¤¨ करते समय à¤à¤• तà¥à¤°à¥à¤Ÿà¤¿ हà¥à¤ˆ." msgid "An error occurred. Please try again later." msgstr "à¤à¤• तà¥à¤°à¥à¤Ÿà¤¿ हो गई. बाद में पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें." msgid "Available" msgstr "उपलबà¥à¤§" msgid "Available Metadata" msgstr "उपलबà¥à¤§ मेटाडाटा" msgid "Back" msgstr "पीछे" msgid "Cancel" msgstr "रदà¥à¤¦ करें" #, python-format msgid "Confirm %s" msgstr "पà¥à¤·à¥à¤Ÿà¤¿ करें %s" msgid "Could not decrypt the password" msgstr "पासवरà¥à¤¡ को डिकà¥à¤°à¤¿à¤ªà¥à¤Ÿ नहीं कर सका" msgid "Could not read the file" msgstr "फ़ाइल को पढ़ नहीं सका" msgid "Danger: " msgstr "खतरा: " msgid "Delete" msgstr "मिटाà¤à¤" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] " %s मद पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤" msgstr[1] " %s मद पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤" msgid "Duplicate keys are not allowed" msgstr "à¤à¤• जैसे कà¥à¤‚जी की अनà¥à¤®à¤¤à¤¿ नहीं है" msgid "Error" msgstr "तà¥à¤°à¥à¤Ÿà¤¿" msgid "Error: " msgstr "तà¥à¤°à¥à¤Ÿà¤¿: " msgid "Existing Metadata" msgstr "मौजूदा मेटाडाटा " msgid "Filter" msgstr "छानना शà¥à¤°à¥‚..." msgid "Flavor" msgstr "फà¥à¤²à¥‡à¤µà¤°" msgid "ID" msgstr "आईडी" msgid "Info" msgstr "सूचना" msgid "Interfaces" msgstr "अंतरफलक" msgid "Loading" msgstr "लोड हो रहा है" msgid "Name" msgstr "नाम" msgid "Next" msgstr "अगला" msgid "No" msgstr "नहीं" msgid "No available metadata" msgstr "कोई मेटाडेटा उपलबà¥à¤§ नहीं है" msgid "No data available." msgstr "कोई आà¤à¤•ड़ा उपलबà¥à¤§ नहीं." msgid "No existing metadata" msgstr "कोई मौजूदा मेटाडाटा नहीं " msgid "No items to display." msgstr "दिखाने के लिठकोई सामगà¥à¤°à¤¿à¤¯à¥‹à¤‚ नहीं." msgid "No roles" msgstr "भूमिकाà¤à¤‚ नहीं है" msgid "None" msgstr "कोई नहीं" msgid "Not authorized to do this operation." msgstr " इस पà¥à¤°à¤•िया को कारà¥à¤¯à¤¾à¤¨à¥à¤µà¤¯à¤¿à¤¤ करने की अनà¥à¤®à¤¤à¤¿ नहीं है" msgid "Notice: " msgstr "सूचना: " msgid "Passwords do not match." msgstr "पासवरà¥à¤¡ मेल नहीं खाते." msgid "Remove" msgstr "हटाà¤à¤" msgid "Roles" msgstr "भूमिका" msgid "STATUS" msgstr "सà¥à¤¥à¤¿à¤¤à¤¿" msgid "Status" msgstr "सà¥à¤¥à¤¿à¤¤à¤¿" msgid "Submit" msgstr "जमा करें" msgid "Success: " msgstr "सफलता:" msgid "There was a problem communicating with the server, please try again." msgstr "सरà¥à¤µà¤° के साथ संवाद सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ करने में कोई समसà¥à¤¯à¤¾ थी, पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें." msgid "There was an error submitting the form. Please try again." msgstr "फारà¥à¤® जमा करने में तà¥à¤°à¥à¤Ÿà¤¿ हà¥à¤ˆ. पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें." msgid "View Details" msgstr "विवरण देखें" msgid "Warning: " msgstr "चेतावनी: " msgid "Working" msgstr "काम चालू है" msgid "Yes" msgstr "हाà¤" #, python-format msgid "You have selected %s. " msgstr "आपने %s का चà¥à¤¨à¤¾à¤µ किया है. " horizon-9.0.0/horizon/locale/nl_NL/0000775000567000056710000000000012701407231020277 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/nl_NL/LC_MESSAGES/0000775000567000056710000000000012701407231022064 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/locale/nl_NL/LC_MESSAGES/django.po0000664000567000056710000001231212701407063023670 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata # Joris S'heeren , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-04 12:43+0000\n" "Last-Translator: Joris S'heeren \n" "Language-Team: Dutch (Netherlands)\n" "Language: nl-NL\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "" "\n" " Login as different user or go back to home page\n" " " msgstr "" "\n" " Meldt u zich aan als een andere gebruiker of ga terug naar " "thuis\n" " " #, python-format msgid "" "\n" " %(start)s" msgstr "" "\n" " %(start)s" #, python-format msgid "" "\n" " %(end)s" msgstr "" "\n" " %(end)s" #, python-format msgctxt "past" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgctxt "present" msgid "%(action)s %(data_type)s" msgstr "%(action)s %(data_type)s" #, python-format msgid "%(action)s: %(objs)s" msgstr "%(action)s: %(objs)s" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s PB" msgstr "%s PB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s completed successfully." msgstr "%s succesvol afgerond." #, python-format msgid "%s did not complete." msgstr "%s was niet voltooid." msgid "« Prev" msgstr "« Prev" msgid "(No Limit)" msgstr "(Geen limiet)" msgid "-" msgstr "-" msgid "0 Bytes" msgstr "0 bytes" #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Een %(resource)s met de naam \"%(name)s\" bestaat al." msgid "Actions" msgstr "Acties" msgid "Active RAM:" msgstr "Actieve RAM:" msgid "Add a row" msgstr "Rij toevoegen" msgid "All available" msgstr "Alle beschikbare" msgid "Available" msgstr "Beschikbaar" msgid "Back" msgstr "Terug" msgid "Cancel" msgstr "Annuleren" msgid "Connect" msgstr "Verbinden" msgid "Delete" msgstr "Verwijderen" msgid "Deleted" msgstr "Verwijderd" msgid "Error: " msgstr "Fout: " msgid "Filter" msgstr "Filter" msgid "Horizon" msgstr "Horizon" msgid "Incorrect format for IP address" msgstr "Onjuist formaat voor IP adres" msgid "Info: " msgstr "Informatie: " msgid "Invalid subnet mask" msgstr "Ongeldig subnet masker" msgid "Invalid version for IP address" msgstr "Ongeldige versie voor IP adres" msgid "Limit Summary" msgstr "Beperk samenvatting" msgid "Log in" msgstr "Aanmelden" msgid "Login" msgstr "Aanmelden" msgid "Members" msgstr "Leden" msgid "More Actions" msgstr "Meer acties" msgid "Navigation Item" msgstr "Navigatie Artikel" msgid "Never" msgstr "Nooit" msgid "Next" msgstr "Volgende" msgid "Next »" msgstr "Volgende »" msgid "No items to display." msgstr "Geen artikelen om weer te geven." #, python-format msgid "No match returned for the id \"%s\"." msgstr "Geen overeenkomsten gevonden voor de id \"%s\"." msgid "No members." msgstr "Geen leden." msgid "None available." msgstr "Geen beschikbaar." msgid "Not a valid IP protocol number" msgstr "Geen geldig IP protocolnummer" msgid "Not a valid port number" msgstr "Geen geldig poortnummer" msgid "One colon allowed in port range" msgstr "Een dubbele punt is toegestaan in het poortbereik" msgid "Other" msgstr "Overig" msgid "Password is not accepted" msgstr "Wachtwoord is niet geaccepteerd" msgid "Please log in to continue." msgstr "Meldt u zich aan om door te gaan." msgid "Please select a row before taking that action." msgstr "Selecteer alstublieft een rij voor het uitvoeren van die actie." msgid "Processing..." msgstr "Bezig met verwerken..." msgid "Save" msgstr "Opslaan" #, python-format msgid "Select a %s to browse." msgstr "Selecteer een %s om te bladeren." msgid "Sign In" msgstr "Aanmelden" msgid "Submit" msgstr "Indienen" msgid "Success: " msgstr "Gelukt: " msgid "Summary" msgstr "Samenvatting" msgid "The date should be in YYYY-mm-dd format." msgstr "De datum dient in het JJJJ-mm-dd formaat te zijn." msgid "This action cannot be undone." msgstr "Deze actie kan niet ongedaan worden gemaakt." #, python-format msgid "Unable to %(action)s: %(objs)s" msgstr "Niet in staat om %(action)s: %(objs)s" msgid "Unauthorized. Please try logging in again." msgstr "Onbevoegd. Meldt u zich opnieuw aan." #, python-format msgid "Unauthorized: %s" msgstr "Onbevoegd: %s" msgid "Usage Summary" msgstr "Samenvatting van verbruik" msgid "Warning: " msgstr "Waarschuwing: " #, python-format msgid "You are not allowed to %(action)s: %(objs)s" msgstr "Het is u niet toegestaan om %(action)s: %(objs)s" #, python-format msgid "You are not authorized to access %s" msgstr "U bent niet gemachtigd voor toegang tot %s" msgid "You do not have permission to access the resource:" msgstr "U ben niet gemachtigd voor toegang tot de bron:" horizon-9.0.0/horizon/locale/nl_NL/LC_MESSAGES/djangojs.po0000664000567000056710000001232012701407063024224 0ustar jenkinsjenkins00000000000000# OpenStack Infra , 2015. #zanata # Joris S'heeren , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-03-15 01:55+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-03-04 01:03+0000\n" "Last-Translator: Joris S'heeren \n" "Language-Team: Dutch (Netherlands)\n" "Language: nl-NL\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%s GB" msgstr "%s GB" #, python-format msgid "%s KB" msgstr "%s KB" #, python-format msgid "%s MB" msgstr "%s MB" #, python-format msgid "%s TB" msgstr "%s TB" #, python-format msgid "%s bytes" msgstr "%s bytes" msgid "-" msgstr "-" msgid "0 GB" msgstr "0 GB" msgid "0 MB" msgstr "0 MB" msgid "Active" msgstr "Actief" msgid "Add Interface" msgstr "Interface Toevoegen" msgid "Added" msgstr "Toegevoegd" msgid "Allocated" msgstr "Toegewezen" msgid "An error occurred while updating." msgstr "Er is een fout opgetreden tijdens het bijwerken." msgid "An error occurred. Please try again later." msgstr "Er is een fout opgetreden. Probeert u het later nog eens." msgid "Available" msgstr "Beschikbaar" msgid "Available Metadata" msgstr "Beschikbare Metadata" msgid "Back" msgstr "Terug" msgid "Cancel" msgstr "Annuleren" msgid "Click to see more details" msgstr "Klik om meer details te zien" msgid "Click to show or hide" msgstr "Klik om te weergeven of verbergen" msgid "Closed" msgstr "Gesloten" msgid "Closing" msgstr "Sluiten" #, python-format msgid "Confirm %s" msgstr "Bevestig %s" msgid "Connecting" msgstr "Verbinden" msgid "Could not decrypt the password" msgstr "Kon het wachtwoord niet ontcijferen" msgid "Could not read the file" msgstr "Kon het bestand niet lezen" msgid "Current Usage" msgstr "Huidig verbruik" msgid "Custom" msgstr "Aangepast" msgid "Danger" msgstr "Gevaar" msgid "Danger: " msgstr "Gevaar:" msgid "Decimal required" msgstr "Decimale vereist" msgid "Delete" msgstr "Verwijderen" msgid "Delete Interface" msgstr "Interface Verwijderen" msgid "Delete Router" msgstr "Router Verwijderen" #, python-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "%s item weergeven" msgstr[1] "%s items weergeven" msgid "Duplicate keys are not allowed" msgstr "Dubbelen sleutels zijn niet toegestaan" msgid "Error" msgstr "Fout" msgid "Error: " msgstr "Fout: " msgid "Example" msgstr "Voorbeeld" msgid "Existing Metadata" msgstr "Bestaande Metadata" msgid "Filter" msgstr "Filter" msgid "Flavor" msgstr "Smaak" msgid "ID" msgstr "ID" msgid "Info" msgstr "Informatie" msgid "Integer required" msgstr "Nummer vereist" msgid "Interfaces" msgstr "Interfaces" msgid "Loading" msgstr "Aan het laden" msgid "Max" msgstr "Max" msgid "Max length" msgstr "Max lengte" msgid "Min" msgstr "Min" msgid "Min length" msgstr "Min lengte" msgid "Name" msgstr "Naam" msgid "Next" msgstr "Volgende" msgid "No" msgstr "Nee" msgid "No Limit" msgstr "Geen limiet" msgid "No available items" msgstr "Geen beschikbare items" msgid "No available metadata" msgstr "Geen beschikbare Metadata" msgid "No data available." msgstr "Geen gegevens beschikbaar." msgid "No existing metadata" msgstr "Geen bestaande Metadata" msgid "No items to display." msgstr "Geen artikelen om weer te geven." msgid "No roles" msgstr "Geen rollen" msgid "None" msgstr "Geen" msgid "Not authorized to do this operation." msgstr "Niet gemachtigd om deze actie uit te voeren." msgid "Notice: " msgstr "Kennisgeving:" msgid "Open" msgstr "Open" msgid "Open Console" msgstr "Open Bedieningspaneel" msgid "Passwords do not match." msgstr "Wachtwoorden komen niet overeen." msgid "Please confirm your selection. " msgstr "Bevestig uw selectie." msgid "Remaining" msgstr "Overgebleven" msgid "Remove" msgstr "Verwijderen" msgid "Required" msgstr "Vereist" msgid "Roles" msgstr "Rollen" msgid "STATUS" msgstr "STATUS" msgid "Select an item from Available items below" msgstr "Selecteer een item van de beschikbare items hier onder" msgid "Select one" msgstr "Selecteer er één" msgid "Status" msgstr "Status" #, python-format msgid "Status: %s" msgstr "Status: %s" msgid "Submit" msgstr "Indienen" msgid "Success" msgstr "Geslaagd" msgid "Success: " msgstr "Gelukt: " msgid "Text" msgstr "Tekst" msgid "There was a problem communicating with the server, please try again." msgstr "" "Er is een fout opgetreden bij het communiceren met de server. Probeert u het " "nog eens." msgid "There was an error submitting the form. Please try again." msgstr "" "Er is een fout opgetreden bij het indienen van het formulier. Probeer het " "nog eens." msgid "Total" msgstr "Totaal" msgid "Unlimited" msgstr "Onbeperkt" msgid "View Details" msgstr "Bekijk Details" msgid "View Instance Details" msgstr "Bekijk exemplaardetails" msgid "View Router Details" msgstr "Bekijk Routerdetails" msgid "Warning" msgstr "Waarschuwing" msgid "Warning: " msgstr "Waarschuwing: " msgid "Working" msgstr "Bezig" msgid "Yes" msgstr "Ja" #, python-format msgid "You have selected %s. " msgstr "U heeft %s geselecteerd." msgid "m1.small" msgstr "m1.small" horizon-9.0.0/horizon/static/0000775000567000056710000000000012701407231017325 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/0000775000567000056710000000000012701407231021322 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/0000775000567000056710000000000012701407231022277 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/q/0000775000567000056710000000000012701407231022537 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/q/q.extensions.spec.js0000664000567000056710000001101212701407063026462 0ustar jenkinsjenkins00000000000000/* * 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'; describe('horizon.framework.util.q.extensions', function () { describe('allSettled', function() { var service, $q, $scope; var failedPromise = function() { var deferred2 = $q.defer(); deferred2.reject('failed'); return deferred2.promise; }; var passedPromise = function() { var deferred1 = $q.defer(); deferred1.resolve('passed'); return deferred1.promise; }; beforeEach(module('horizon.framework.util.q')); beforeEach(inject(function($injector, _$rootScope_) { service = $injector.get('horizon.framework.util.q.extensions'); $q = $injector.get('$q'); $scope = _$rootScope_.$new(); })); it('should define allSettled', function () { expect(service.allSettled).toBeDefined(); }); it('should resolve all given promises', function() { service.allSettled([{ promise: failedPromise(), context: '1' }, { promise: passedPromise(), context: '2' }]).then(onAllSettled); $scope.$apply(); function onAllSettled(resolvedPromises) { expect(resolvedPromises.fail.length).toEqual(1); expect(resolvedPromises.fail[0]).toEqual({data: 'failed', context: '1'}); expect(resolvedPromises.pass.length).toEqual(1); expect(resolvedPromises.pass[0]).toEqual({data: 'passed', context: '2'}); } }); it('should maintain order of promises regardless of resolve/reject order', function() { var defs = [$q.defer(), $q.defer(), $q.defer(), $q.defer(), $q.defer(), $q.defer()]; service.allSettled([{ promise: defs[0].promise, context: '1' },{ promise: defs[1].promise, context: '2' },{ promise: defs[2].promise, context: '3' },{ promise: defs[3].promise, context: '4' },{ promise: defs[4].promise, context: '5' },{ promise: defs[5].promise, context: '6' }]).then(onAllSettled); defs[1].reject(); defs[2].resolve(); defs[0].resolve(); defs[5].reject(); defs[3].reject(); defs[4].resolve(); $scope.$apply(); function onAllSettled(resolvedPromises) { var pass = resolvedPromises.pass; var fail = resolvedPromises.fail; expect(pass.length).toBe(3); expect(fail.length).toBe(3); expect(pass[0].context).toBe('1'); expect(pass[1].context).toBe('3'); expect(pass[2].context).toBe('5'); expect(fail[0].context).toBe('2'); expect(fail[1].context).toBe('4'); expect(fail[2].context).toBe('6'); } }); }); describe('booleanAsPromise', function() { var service, $scope; beforeEach(module('horizon.framework.util.q')); beforeEach(inject(function($injector, _$rootScope_) { service = $injector.get('horizon.framework.util.q.extensions'); $scope = _$rootScope_.$new(); })); it('should define booleanAsPromise', function () { expect(service.booleanAsPromise).toBeDefined(); }); it('should reject the promise if condition does not evaluates to true', function() { var testValues = [ false, null, {}, 'A', 7 ]; var rejectCount = 0; testValues.map(function doTest(testValue) { service.booleanAsPromise(testValue).then(angular.noop, function failTest() { rejectCount++; }); $scope.$apply(); }); expect(rejectCount).toEqual(testValues.length); }); it('should resolve the promise only if condition to true', function() { var passCount = 0; service.booleanAsPromise(true).then(function passTest() { passCount++; }); $scope.$apply(); expect(passCount).toEqual(1); }); }); }); })(); horizon-9.0.0/horizon/static/framework/util/q/q.module.js0000664000567000056710000000152512701407063024627 0ustar jenkinsjenkins00000000000000/* * 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'; /** * @ngdoc overview * @name horizon.framework.util.q * @description * * # horizon.framework.util.q * * This module provides extensions to the Angular $q service. * */ angular .module('horizon.framework.util.q', []); })(); horizon-9.0.0/horizon/static/framework/util/q/q.module.spec.js0000664000567000056710000000143312701407063025556 0ustar jenkinsjenkins00000000000000/* * 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'; describe('horizon.framework.util.q module', function () { it('should have been defined', function () { expect(angular.module('horizon.framework.util.q')).toBeDefined(); }); }); })(); horizon-9.0.0/horizon/static/framework/util/q/q.extensions.js0000664000567000056710000001143112701407063025536 0ustar jenkinsjenkins00000000000000/* * 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('horizon.framework.util.q') .factory('horizon.framework.util.q.extensions', qExtensions); qExtensions.$inject = ['$q']; /** * @ngdoc factory * @name horizon.framework.util.q:extensions * @module horizon.framework.util.q * @kind function * @description * * Extends the $q from Angular to provide additional functionality. * */ function qExtensions($q) { var service = { allSettled: allSettled, booleanAsPromise: booleanAsPromise }; return service; /** * Allow all given promises to settle and returns a collection * of the successful and failed responses allowing the caller * to make decisions based on the individual results. * * This function is typically used if you need all promises * to complete and get all of the success and response messages, * but need to correlate specific success and failure results * to a particular context. To do this, ensure that the passed * in promise has a context attribute available on the input * promise. Each result or failure reason will include the * context in the fail or pass array. * * It will always result in a success callback (resolved), * but will provide a summary object of the results in a pair * of arrays ("pass" and "fail"), even if some of the promises fail. * The "pass" array will contain each of the results of the * successfully resolved promises and the "fail" array * will return each of the reasons for the rejected promises. * * In contrast to the `$q.all` in Angular which will terminate all * promises if any promise is rejected, this will wait for all promises * to settle. * * The order of the resolve or rejection reasons correlates directly to * the order of the promises in the list. * * @param {array} promiseList * The list of promises to resolve * * @return {object} * An object with 2 lists, one for promises that got resolved * and one for promises that got rejected. * * @example * ``` * var settledPromises = qExtenstions.allSettled([ * {promise: promise1, context: context1}, * {promise: promise2, context: context2} * ]); * settledPromises.then(onSettled); * * function onSettled(data) { * doSomething(data.pass); * doSomething(data.fail); * } * * function doSomething(resolvedList) { * resolvedList.forEach(function (item) { * console.log("context", item.context, "result", item.data); * }); * } */ function allSettled(promiseList) { var deferred = $q.defer(); var passList = []; var failList = []; var promises = promiseList.map(resolveSingle); $q.all(promises).then(onComplete); return deferred.promise; function resolveSingle(singlePromise, index) { var deferredInner = $q.defer(); singlePromise.promise.then(onResolve, onReject); return deferredInner.promise; function onResolve(response) { passList[index] = formatResponse(response, singlePromise.context); deferredInner.resolve(); } function onReject(response) { failList[index] = formatResponse(response, singlePromise.context); deferredInner.resolve(); } function formatResponse(response, context) { return { data: response, context: context }; } } function onComplete() { deferred.resolve({pass: condense(passList), fail: condense(failList)}); } function condense(promiseList) { return promiseList.filter(function removeEmpty(promise) { return !!promise; }); } } /** * Create a promise that resolves if true, otherwise is rejected. * * @param {expression} value * the boolean value to test * * @return {promise} * the promise object */ function booleanAsPromise(value) { var deferred = $q.defer(); if (value === true) { deferred.resolve(); } else { deferred.reject(); } return deferred.promise; } } })(); horizon-9.0.0/horizon/static/framework/util/extensible/0000775000567000056710000000000012701407231024441 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/extensible/extensible.service.spec.js0000664000567000056710000001346012701407063031540 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; describe('horizon.framework.util.extensible module', function () { it('should have been defined', function () { expect(angular.module('horizon.framework.util.extensible')).toBeDefined(); }); }); describe('extensible service', function () { var extensibleService, container, items; beforeEach(module('horizon.framework.util.extensible')); beforeEach(inject(function ($injector) { extensibleService = $injector.get('horizon.framework.util.extensible.service'); container = {}; items = [ { id: '1' }, { id: '2' }, { id: '3' } ]; extensibleService(container, items); })); it('can append items', function () { expect(items.length).toBe(3); var item4 = { id: '4' }; container.append(item4, 1); expect(items.length).toBe(4); expect(items[3]).toBe(item4); var item5 = { id: '5' }; container.append(item5); expect(items.length).toBe(5); expect(items[3]).toBe(item5); expect(items[4]).toBe(item4); var item6 = { id: '6' }; container.append(item6, 2); expect(items.length).toBe(6); expect(items[3]).toBe(item5); expect(items[4]).toBe(item6); expect(items[5]).toBe(item4); var item7 = { id: '7' }; container.append(item7, 1); expect(items.length).toBe(7); expect(items[3]).toBe(item5); expect(items[4]).toBe(item6); expect(items[5]).toBe(item4); expect(items[6]).toBe(item7); var item8 = { id: '8' }; container.append(item8); expect(items.length).toBe(8); expect(items[3]).toBe(item5); expect(items[4]).toBe(item8); expect(items[5]).toBe(item6); expect(items[6]).toBe(item4); expect(items[7]).toBe(item7); }); it('can prepend items', function () { expect(items.length).toBe(3); var item4 = { id: '4' }; container.prepend(item4, 1); expect(items.length).toBe(4); expect(items[0]).toBe(item4); var item5 = { id: '5' }; container.prepend(item5); expect(items.length).toBe(5); expect(items[0]).toBe(item4); expect(items[1]).toBe(item5); var item6 = { id: '6' }; container.prepend(item6, 2); expect(items.length).toBe(6); expect(items[0]).toBe(item4); expect(items[1]).toBe(item6); expect(items[2]).toBe(item5); var item7 = { id: '7' }; container.prepend(item7, 1); expect(items.length).toBe(7); expect(items[0]).toBe(item7); expect(items[1]).toBe(item4); expect(items[2]).toBe(item6); expect(items[3]).toBe(item5); var item8 = { id: '8' }; container.prepend(item8); expect(items.length).toBe(8); expect(items[0]).toBe(item7); expect(items[1]).toBe(item4); expect(items[2]).toBe(item6); expect(items[3]).toBe(item8); expect(items[4]).toBe(item5); }); it('can insert items', function () { expect(items.length).toBe(3); var item4 = { id: '4' }; container.after('1', item4, 1); expect(items.length).toBe(4); expect(items[1]).toBe(item4); var item5 = { id: '5' }; container.after('1', item5); expect(items.length).toBe(5); expect(items[1]).toBe(item4); expect(items[2]).toBe(item5); var item6 = { id: '6' }; container.after('1', item6, 2); expect(items.length).toBe(6); expect(items[1]).toBe(item4); expect(items[2]).toBe(item6); expect(items[3]).toBe(item5); var item7 = { id: '7' }; container.after('1', item7, 1); expect(items.length).toBe(7); expect(items[1]).toBe(item7); expect(items[2]).toBe(item4); expect(items[3]).toBe(item6); expect(items[4]).toBe(item5); var item8 = { id: '8' }; container.after('1', item8); expect(items.length).toBe(8); expect(items[1]).toBe(item7); expect(items[2]).toBe(item4); expect(items[3]).toBe(item6); expect(items[4]).toBe(item8); expect(items[5]).toBe(item5); var last = { id: 'last' }; container.after('3', last); expect(items.length).toBe(9); expect(items[8]).toBe(last); var insert = function() { container.after('foo', { id: 'bar' }); }; expect(insert).toThrowError(Error, 'Item with id foo not found.'); }); it('can remove items', function () { expect(items.length).toBe(3); container.remove('2'); expect(items.length).toBe(2); container.remove('1'); expect(items.length).toBe(1); var remove = function() { container.remove('foo'); }; expect(remove).toThrowError(Error, 'Item with id foo not found.'); }); it('can replace items', function () { expect(items.length).toBe(3); var item4 = { id: '4' }; container.replace('2', item4); expect(items.length).toBe(3); expect(items[1]).toBe(item4); var item5 = { id: '5' }; container.replace('1', item5); expect(items.length).toBe(3); expect(items[0]).toBe(item5); expect(items[1]).toBe(item4); expect(items[2].id).toBe('3'); var replace = function() { container.replace('foo', { id: 'bar' }); }; expect(replace).toThrowError(Error, 'Item with id foo not found.'); }); it('can add controllers', function () { expect(container.controllers.length).toBe(0); container.addController('MyController'); expect(container.controllers.length).toBe(1); }); it('can chain method calls', function () { container .append({ id: '4' }) .prepend({ id: '5' }) .after('1', { id: '6' }) .remove('3') .replace('2', { id: '7' }) .addController('foo'); expect(items.map(function getId(item) { return item.id; })).toEqual(['5', '1', '6', '7', '4']); }); }); })(); horizon-9.0.0/horizon/static/framework/util/extensible/extensible.service.js0000664000567000056710000002332312701407063030606 0ustar jenkinsjenkins00000000000000/* * Copyright 2015 IBM Corp. * * 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'; var $controller; angular .module('horizon.framework.util.extensible') .factory('horizon.framework.util.extensible.service', extensibleService); extensibleService.$inject = [ '$controller' ]; /** * @ngdoc service * @name horizon.framework.util.extensible.service:extensibleService * @module horizon.framework.util.extensible.service * @kind function * @description * * Make a container extensible by decorating it with functions that allow items to be added * or removed. * * @returns {Function} A function used to decorate a container to make it extensible. */ function extensibleService(_$controller_) { $controller = _$controller_; return makeExtensible; } /** * A decorator function that makes the given container object extensible by allowing items to * be added or removed. This can be used on any object that contains multiple items where a user * might want to insert or remove their own items. Examples include workflow steps, table * actions, and form fields. Each item must have a unique ID within the container. It also adds * the ability to add controllers in the scope of the container. The following functions are * added to the container: * * append(item, priority) * prepend(item, priority) * after(id, item, priority) * remove(id) * replace(id, item) * addController(controller) * initControllers($scope) * * Priorities are optional and determine the priority for multiple items placed at the same * position. Higher numbers mean lower priority. If not provided the item will have the lowest * priority (infinity). * * @param {Object} container - The container object to make extensible. * @param {Object} items - An array of all items in the container in display order. Each item * should be an object and must have an id property that uniquely identifies the item within * the container. * * For example, to make a workflow extensible: * * extensibleService(workflow, workflow.steps); */ function makeExtensible(container, items) { /** * Append a new item at the end of the container's items. * * @param {Object} item The item to append. * @param {Number} priority The optional priority for placement at the end of the container. * Lower priority (higher number) items will be placed at the end but before higher priority * (lower number) items. * @returns {Object} The container, to allow for chaining. */ container.append = function(item, priority) { if (!angular.isNumber(priority)) { priority = Infinity; } var itemsByPosition = getItemsByPosition(items, 'last').reverse(); var index = items.length; for (var i = 0; i < itemsByPosition.length; i++) { if (priority > itemsByPosition[i]._ext.priority) { index = getItemIndex(items, itemsByPosition[i].id); break; } } item._ext = {position: 'last', priority: priority}; items.splice(index, 0, item); return container; }; /** * Add a new item at the beginning of the container's items. * * @param {Object} item The item to add at the front. * @param {Number} priority The optional priority for placement at the front of the container. * Lower priority (higher number) items will be placed at the front but after higher priority * (lower number) items. * @returns {Object} The container, to allow for chaining. */ container.prepend = function(item, priority) { if (!angular.isNumber(priority)) { priority = Infinity; } var itemsByPosition = getItemsByPosition(items, 'first'); var index = itemsByPosition.length; for (var i = 0; i < itemsByPosition.length; i++) { if (priority <= itemsByPosition[i]._ext.priority) { index = getItemIndex(items, itemsByPosition[i].id); break; } } item._ext = {position: 'first', priority: priority}; items.splice(index, 0, item); return container; }; /** * Add a new item after the item with the given id. * * @param {String} id The id of an existing item in the container. The new item will be placed * after this item. * @param {Object} item The item to insert. * @param {Number} priority The optional priority for placement in the container at this * position. Higher priority (lower number) items will be placed more closely after the * given item id, followed by lower priority (higher number) items. * @returns {Object} The container, to allow for chaining. */ container.after = function(id, item, priority) { if (!angular.isNumber(priority)) { priority = Infinity; } var itemsByPosition = getItemsByPosition(items, 'after-' + id); var index = getItemIndex(items, id) + itemsByPosition.length + 1; for (var i = 0; i < itemsByPosition.length; i++) { if (priority <= itemsByPosition[i]._ext.priority) { index = getItemIndex(items, itemsByPosition[i].id); break; } } item._ext = {position: 'after-' + id, priority: priority}; items.splice(index, 0, item); return container; }; /** * Remove an item from the container and return its index. When removing items from the * container you will need to account for any data the item might have been contributing to * the container's model. A custom controller could be used for this purpose and added using * the addController function. * * @param {String} id The id of the item to remove. * @returns {Object} The container, to allow for chaining. */ container.remove = function(id) { var index = getItemIndex(items, id); items.splice(index, 1); return container; }; /** * Replace an item in the container with the one provided. The new item will need to account * for any data the original item might have been contributing to the container's model. * * @param {String} id The id of an existing item in the container. The item with this id will * be removed and the new item will be inserted in its place. * @param {Object} item The item to insert. * @returns {Object} The container, to allow for chaining. */ container.replace = function(id, item) { var index = getItemIndex(items, id); container.remove(id); items.splice(index, 0, item); return container; }; /** * The controllers array keeps track of all controllers that should be instantiated with the * scope of the container. */ container.controllers = []; /** * When an extensible container is instantiated, it should call this function to initialize * any additional controllers added by plugins. A typical plugin itself should not need to * call this, since any extensible containers created in horizon should be doing this. */ container.initControllers = function($scope) { angular.forEach(container.controllers, function(ctrl) { $controller(ctrl, {$scope: $scope}); }); }; /** * Add a custom controller to be instantiated with the scope of the container when a container * instance is created. This is useful in cases where a plugin removes an item or otherwise * wants to make changes to a container without adding any items. For example, to add some * custom validation to an existing item or react to certain container events. * * @param {String} ctrl The controller to add, e.g. 'MyFeatureController'. * @returns {Object} The container, to allow for chaining. */ container.addController = function(ctrl) { container.controllers.push(ctrl); return container; }; } /** * Get an array of items that have been added at a given position. * * @param {Array} items An array of all items in the container. * @param {String} position The position of the items to return. This can be "first", * "last", or "after-". * * @returns {Array} An array of items. The returned items are sorted by priority. If * there are no items for the given position an empty array is returned. If two items have * the same priority then the last one added will "win". This is so the returned items are * always in the proper order for display purposes. */ function getItemsByPosition(items, position) { return items.filter(function filterItems(item) { return item._ext && item._ext.position === position; }).sort(function sortItems(a, b) { return a._ext.priority - b._ext.priority || 1; }); } /** * Get the index of a given item. * * @param {Array} items An array of all items in the container. * @param {String} id The id of an item. The index of the item will be returned. * * @returns {Number} The index of the item with the given id. */ function getItemIndex(items, id) { for (var i = 0; i < items.length; i++) { if (items[i].id === id) { return i; } } throw new Error(interpolate('Item with id %(id)s not found.', {id: id}, true)); } })(); horizon-9.0.0/horizon/static/framework/util/extensible/extensible.module.js0000664000567000056710000000164712701407063030440 0ustar jenkinsjenkins00000000000000/* * Copyright 2015 IBM Corp. * * 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'; /** * @ngdoc overview * @name horizon.framework.util.extensible * @description * * # horizon.framework.util.extensible * * This module provides a service to allow various UI components to be extended by plugins. */ angular .module('horizon.framework.util.extensible', []); })(); horizon-9.0.0/horizon/static/framework/util/tech-debt/0000775000567000056710000000000012701407231024136 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/tech-debt/tech-debt.module.js0000664000567000056710000000123212701407063027620 0ustar jenkinsjenkins00000000000000/* * * 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('horizon.framework.util.tech-debt', []); })(); horizon-9.0.0/horizon/static/framework/util/tech-debt/dummy.controller.js0000664000567000056710000000130712701407063030015 0ustar jenkinsjenkins00000000000000/* * * 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('horizon.framework.util.tech-debt') .controller('DummyController', angular.noop); })(); horizon-9.0.0/horizon/static/framework/util/tech-debt/helper-functions.service.spec.js0000664000567000056710000000311312701407063032352 0ustar jenkinsjenkins00000000000000/* * * 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'; describe('horizon.framework.util.tech-debt.helper-functions', function () { beforeEach(function () { angular.mock.module('horizon.framework.util.tech-debt'); }); var hzUtils; beforeEach(function () { angular.mock.inject(function ($injector) { hzUtils = $injector.get('horizon.framework.util.tech-debt.helper-functions'); }); }); describe('loadAngular', function () { var rootScope, element; beforeEach(function () { element = angular.element('
'); angular.mock.inject(function ($injector) { rootScope = $injector.get('$rootScope'); }); spyOn(rootScope, '$apply'); }); it('should call a compile and apply ', function () { hzUtils.loadAngular(element); //checks the use of apply function expect(rootScope.$apply).toHaveBeenCalled(); //checks the use of compile function expect(element.hasClass('ng-scope')).toBeTruthy(); }); }); }); })(); horizon-9.0.0/horizon/static/framework/util/tech-debt/helper-functions.service.js0000664000567000056710000000234012701407063031422 0ustar jenkinsjenkins00000000000000/* * * 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('horizon.framework.util.tech-debt') .factory('horizon.framework.util.tech-debt.helper-functions', helperFunctionsService); helperFunctionsService.$inject = ['$rootScope', '$compile']; function helperFunctionsService($rootScope, $compile) { var service = { loadAngular: loadAngular }; return service; /* * Compile angular directives in a DOM element that has typically been * loaded into the page (the only current use of this is in JQuery * modal dialogs). */ function loadAngular(element) { $compile(element)($rootScope); $rootScope.$apply(); } } })(); horizon-9.0.0/horizon/static/framework/util/tech-debt/image-file-on-change.directive.js0000664000567000056710000000213712701407063032313 0ustar jenkinsjenkins00000000000000/* * * 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('horizon.framework.util.tech-debt') .directive('imageFileOnChange', imageFileOnChange); function imageFileOnChange() { var directive = { require: 'ngModel', restrict: 'A', link: link }; return directive; function link($scope, element, attrs, ngModel) { element.bind('change', function (event) { var files = event.target.files; var file = files[0]; ngModel.$setViewValue(file); $scope.$apply(); }); } } })(); horizon-9.0.0/horizon/static/framework/util/promise-toggle/0000775000567000056710000000000012701407231025234 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/promise-toggle/hz-promise-toggle.directive.spec.js0000664000567000056710000001050612701407063034061 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; describe('horizon.framework.util.promise-toggle', function () { describe('directive:hz-promise-toggle-mock', function () { var $compile, $q, $scope, mockService, baseElement; var $mockHtmlInput = '$mock-input'; var baseHtml = [ '
', '
', '
', '
', '
', '
' ].join(''); beforeEach(function() { mockService = { mockResolver: function(shouldResolve) { var deferred = $q.defer(); if (shouldResolve === 'true') { deferred.resolve(); } else { deferred.reject(); } return deferred.promise; } }; spyOn(mockService, 'mockResolver').and.callThrough(); module('horizon.framework.util.promise-toggle', function ($provide) { $provide.value('mockService', mockService); }); inject(function (_$compile_, _$q_, _$rootScope_) { $compile = _$compile_; $q = _$q_; $scope = _$rootScope_.$new(); baseElement = null; }); }); it('should evaluate the given attribute name', function () { $scope.test = { retVal: 'true' }; var params = 'test.retVal'; var template = baseHtml.replace($mockHtmlInput, params); baseElement = $compile(template)($scope); shouldHaveCompiledContent(true); expect(mockService.mockResolver).toHaveBeenCalledWith('true'); }); it('should be compiled for one resolved promise', function () { var params = '\"true\"'; var template = baseHtml.replace($mockHtmlInput, params); baseElement = $compile(template)($scope); shouldHaveCompiledContent(true); expect(mockService.mockResolver).toHaveBeenCalledWith('true'); }); it('should be compiled for multiple resolved promises', function () { var params = '[\"true\", \"true\"]'; var template = baseHtml.replace($mockHtmlInput, params); baseElement = $compile(template)($scope); shouldHaveCompiledContent(true); expect(mockService.mockResolver).toHaveBeenCalledWith('true'); expect(mockService.mockResolver.calls.count()).toEqual(2); }); it('should not be compiled for one rejected promise', function () { var params = '\"false\"'; var template = baseHtml.replace($mockHtmlInput, params); baseElement = $compile(template)($scope); shouldHaveCompiledContent(false); expect(mockService.mockResolver).toHaveBeenCalledWith('false'); }); it('should not be compiled for mixed resolved & rejected promise', function () { var params = '[\"true\", \"false\", \"true\"]'; var template = baseHtml.replace($mockHtmlInput, params); baseElement = $compile(template)($scope); shouldHaveCompiledContent(false); expect(mockService.mockResolver).toHaveBeenCalledWith('true'); expect(mockService.mockResolver).toHaveBeenCalledWith('false'); expect(mockService.mockResolver.calls.count()).toEqual(3); }); function shouldHaveCompiledContent(shouldInclude) { $scope.$apply(); var baseElementChildren = baseElement.children(); if (shouldInclude) { var includedContent = baseElementChildren.first(); expect(includedContent.hasClass('ng-scope')).toBe(true); expect(includedContent.children().first().hasClass('child-element')).toBe(true); } else { expect(baseElementChildren.length).toBe(0); } } }); }); })(); horizon-9.0.0/horizon/static/framework/util/promise-toggle/hz-promise-toggle.directive.js0000664000567000056710000001153612701407063033134 0ustar jenkinsjenkins00000000000000/** * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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('horizon.framework.util.promise-toggle') .directive('hzPromiseToggleTemplate', hzPromiseToggleTemplate); /** * @ngdoc directive * @name horizon.framework.util.promise-toggle:directive:hzPromiseToggleTemplate * @module horizon.framework.util.promise-toggle * @description * * A template directive that can be used for creating additional directives * which will only compile their transcluded content if all of the promises * associated with the directive resolve. * * This supports declarative directives that compile in their * content only after the related promises resolve. This is typically * intended to be used for very fast checks (often cached) of whether or * not a particular setting or required service is enabled. This should not * be used for checks that have frequently changing content, because this * is evaluated once per page load. * * The actual name of whatever directive this is after being extended * (angular.extend) should be set to the input that will be passed * into the promise resolver. The promise resolver will either resolve * or reject the promise. If it resolves, the content inside of the * element will be linked in. Otherwise, it will be removed completely. * When the input is an array, each element of the array will be treated * as a distinct input and a single promise resolver will be invoked for * each input element. When that is done, all promises must resolve in * order for the content to be linked in. If any of them are rejected, * the element will be removed. * * When extending, the name and singlePromiseResolver must be specified. * Other properties may also be overridden for additional customization. * * @example * * To use this, simply create a concrete directive using angular.extend: * * angular * .module('horizon.framework.util.promise-toggle') * .directive('hzPromiseToggleMock', hzPromiseToggleMock); * * hzPromiseToggleMock.$inject = [ * 'hzPromiseToggleTemplateDirective', * 'mockService' * ]; * * function hzPromiseToggleMock(hzPromiseToggleTemplateDirective, mockService) { * return angular.extend( * hzPromiseToggleTemplateDirective[0], * { * singlePromiseResolver: mockService.mockResolver, * name: 'hzPromiseToggleMock' * } * ); * } * * Then in the HMTL: * *
Your content
* For single input / single promise resolution: * *
Your content
* For multiple input / multiple promise resolution: * */ hzPromiseToggleTemplate.$inject = ['$q', '$parse']; function hzPromiseToggleTemplate($q, $parse) { var directive = { name: null, singlePromiseResolver: null, transclude: 'element', priority: 2000, terminal: true, restrict: 'A', compile: compile, $$tlb: true //eslint-disable-line angular/no-private-call }; return directive; //////////////// function compile(element, attrs, linker) { var input = $parse(attrs[this.name]); var singlePromiseResolver = this.singlePromiseResolver; return resolvePromises; //////////////// function resolvePromises(scope, iterStartElement) { var resolvedInput = input(scope); var promiseResolver = angular.isArray(resolvedInput) ? multiPromiseResolver(singlePromiseResolver, resolvedInput) : singlePromiseResolver(resolvedInput); promiseResolver.then(linkContent, removeContent); function linkContent() { linker(scope, function (clone) { iterStartElement.after(clone); }); } function removeContent() { element.remove(); } function multiPromiseResolver(resolver, arrayInput) { // Resolves each individual input against the promise resolver. // If any fail, all will fail. return $q.all( arrayInput.map(function (singleInput) { return resolver(singleInput); }) ); } } } } })(); horizon-9.0.0/horizon/static/framework/util/promise-toggle/hz-promise-toggle.directive.mock.js0000664000567000056710000000343512701407063034063 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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('horizon.framework.util.promise-toggle') .directive('hzPromiseToggleMock', hzPromiseToggleMock); hzPromiseToggleMock.$inject = [ 'hzPromiseToggleTemplateDirective', 'mockService' ]; /** * @ngdoc directive * @name horizon.framework.util.promise-toggle:hzPromiseToggleMock * @module horizon.framework.util.promise-toggle * @description * * This allows testing the promise toggle directive in the way * that it is intended to be used. It also provides a usage example. * * @example * ```html
``` */ function hzPromiseToggleMock(hzPromiseToggleTemplateDirective, mockService) { return angular.extend( hzPromiseToggleTemplateDirective[0], { singlePromiseResolver: mockService.mockResolver, name: 'hzPromiseToggleMock' } ); } })(); horizon-9.0.0/horizon/static/framework/util/promise-toggle/promise-toggle.module.js0000664000567000056710000000213312701407063032015 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc overview * @name horizon.framework.util.promise-toggle * @description * * This supports declarative directives that compile in their * content only after the related promises resolve. This is typically * intended to be used for very fast checks (often cached) of whether or * not a particular setting or required service is enabled. */ angular .module('horizon.framework.util.promise-toggle', []); })(); horizon-9.0.0/horizon/static/framework/util/validators/0000775000567000056710000000000012701407231024447 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/validators/validators.spec.js0000664000567000056710000001374512701407063030123 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; describe('horizon.framework.util.validators module', function () { it('should have been defined', function () { expect(angular.module('horizon.framework.util.validators')).toBeDefined(); }); }); describe('validators directive', function () { beforeEach(module('horizon.framework')); describe('validateNumberMax directive', function () { var $scope, $form; beforeEach(inject(function ($injector) { var $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); $scope.count = 0; var markup = '
' + '' + '
'; $compile(angular.element(markup))($scope); $form = $scope.testForm; $scope.$apply(); })); it('should pass validation initially when count is 0 and max is 1', function () { expect($form.count.$valid).toBe(true); expect($form.$valid).toBe(true); }); it('should not pass validation if count increased to 2 and max is 1', function () { $form.count.$setViewValue(2); $scope.$apply(); expect($form.count.$valid).toBe(false); expect($form.$valid).toBe(false); }); it('should pass validation if count is empty', function () { $form.count.$setViewValue(''); $scope.$apply(); expect($form.count.$valid).toBe(true); expect($form.$valid).toBe(true); }); }); describe('validateNumberMin directive', function () { var $scope, $form; beforeEach(inject(function ($injector) { var $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); $scope.count = 0; var markup = '
' + '' + '
'; $compile(angular.element(markup))($scope); $form = $scope.testForm; $scope.$apply(); })); it('should not pass validation initially when count is 0 and min is 1', function () { expect($form.count.$valid).toBe(false); expect($form.$valid).toBe(false); }); it('should pass validation if count increased to 2 and min is 1', function () { $form.count.$setViewValue(2); $scope.$apply(); expect($scope.count).toBe(2); expect($form.count.$valid).toBe(true); expect($form.$valid).toBe(true); }); it('should pass validation if count is empty', function () { $form.count.$setViewValue(''); $scope.$apply(); expect($form.count.$valid).toBe(true); expect($form.$valid).toBe(true); }); }); describe('validateUnique directive', function () { var scope, port, name, protocol; var items = [{ id: '1', protocol: 'HTTP' }, { id: '2', protocol: 'HTTPS' }, { id: '3', protocol: 'TCP' }]; var markup = '
' + '' + '' + '' + '
'; function protocolIsUnique(value) { return !items.some(function(item) { return item.protocol === value; }); } beforeEach(inject(function ($injector) { var compile = $injector.get('$compile'); scope = $injector.get('$rootScope').$new(); // generate dom from markup var element = compile(markup)(scope); port = element.children('#port'); name = element.children('#name'); protocol = element.children('#protocol'); // set initial data scope.ports = [80, 443]; scope.names = ['name1', 'name2']; scope.protocolIsUnique = protocolIsUnique; scope.$apply(); })); it('should be initially empty', function () { expect(port.val()).toEqual(''); expect(name.val()).toEqual(''); expect(protocol.val()).toEqual(''); expect(port.hasClass('ng-valid')).toBe(true); expect(name.hasClass('ng-valid')).toBe(true); expect(protocol.hasClass('ng-valid')).toBe(true); }); it('should be invalid if values are not unique', function () { scope.port = 80; scope.name = 'name1'; scope.protocol = 'TCP'; scope.$apply(); expect(port.hasClass('ng-valid')).toBe(false); expect(name.hasClass('ng-valid')).toBe(false); expect(protocol.hasClass('ng-valid')).toBe(false); }); it('should be valid if values are unique', function () { scope.port = 81; scope.name = 'name3'; scope.protocol = 'TERMINATED_HTTPS'; scope.$apply(); expect(port.hasClass('ng-valid')).toBe(true); expect(name.hasClass('ng-valid')).toBe(true); expect(protocol.hasClass('ng-valid')).toBe(true); }); it('should be valid if param is undefined', function () { delete scope.ports; scope.port = 80; scope.$apply(); expect(port.hasClass('ng-valid')).toBe(true); }); it('should be valid if param is a string', function () { scope.ports = '80'; scope.port = 80; scope.$apply(); expect(port.hasClass('ng-valid')).toBe(true); }); it('should be valid if param is a number', function () { scope.ports = 80; scope.port = 80; scope.$apply(); expect(port.hasClass('ng-valid')).toBe(true); }); it('should be valid if param is an object', function () { scope.ports = { port: 80 }; scope.port = 80; scope.$apply(); expect(port.hasClass('ng-valid')).toBe(true); }); }); }); })(); horizon-9.0.0/horizon/static/framework/util/validators/validate-unique.js0000664000567000056710000000502012701407063030102 0ustar jenkinsjenkins00000000000000/* * Copyright 2016 IBM Corp. * * 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'; /** * @ngdoc directive * @name horizon.framework.util.validators:validateUnique * @element ng-model * @description * The `validateUnique` directive provides validation for form input elements to ensure * values are unique. * * The form input value can be validated against an array of values or a custom validator * function can be provided. If an array of values is specified, the validator returns true * if the value is not found in the array. If a function is specified, the validator returns * the value of the function which should evaluate to true or false. * * @restrict A * * @example * ``` * * ``` * * @example * ``` * * * ctrl.validateUniqueName = function(value) { * return !existingItems.some(function(item) { * return item.leaf && item.leaf.name === value; * }); * }; * ``` */ angular .module('horizon.framework.util.validators') .directive('validateUnique', validateUnique); function validateUnique() { var directive = { require: 'ngModel', restrict: 'A', link: link }; return directive; ////////// function link(scope, element, attrs, ctrl) { ctrl.$parsers.push(validate); ctrl.$formatters.push(validate); attrs.$observe('validateUnique', function () { validate(ctrl.$modelValue); }); function validate(value) { var param = scope.$eval(attrs.validateUnique); var unique = true; if (angular.isArray(param) && param.length > 0) { unique = param.indexOf(value) < 0; } else if (angular.isFunction(param)) { unique = param(value); } ctrl.$setValidity('unique', unique); return value; } } } })(); horizon-9.0.0/horizon/static/framework/util/validators/validators.module.js0000664000567000056710000000266312701407063030453 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc overview * @name horizon.framework.util.validators * @description * * # horizon.framework.util.validators * * The `horizon.framework.util.validators` module provides support for validating * forms. * * | Directives | * |---------------------------------------------------------------------------------| * | {@link horizon.framework.util.validators.directive:validateNumberMax `validateNumberMax`} | * | {@link horizon.framework.util.validators.directive:validateNumberMin `validateNumberMin`} | * | {@link horizon.framework.util.validators.directive:hzPasswordMatch `hzPasswordMatch`} | * */ angular .module('horizon.framework.util.validators', []); })(); horizon-9.0.0/horizon/static/framework/util/validators/hz-password-match.directive.spec.js0000664000567000056710000000536212701407063033277 0ustar jenkinsjenkins00000000000000/** * Copyright 2015 IBM Corp. * * 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'; describe('hzPasswordMatch directive', function () { var $compile, $rootScope, element, password, cpassword; var markup = '
' + '' + '' + '
'; beforeEach(module('horizon.framework.widgets')); beforeEach(module('horizon.framework.util.validators')); beforeEach(inject(function ($injector) { $compile = $injector.get('$compile'); $rootScope = $injector.get('$rootScope').$new(); // generate dom from markup element = $compile(markup)($rootScope); password = element.children('input[name]'); cpassword = element.children('input[hz-password-match]'); // setup up initial data $rootScope.user = {}; $rootScope.$apply(); })); it('should be initially empty', function () { expect(password.val()).toEqual(''); expect(password.val()).toEqual(cpassword.val()); expect(cpassword.hasClass('ng-valid')).toBe(true); }); it('should not match if user changes only password', function () { $rootScope.user.password = 'password'; $rootScope.$apply(); cpassword.change(); expect(cpassword.val()).not.toEqual(password.val()); }); it('should not match if user changes only confirmation password', function () { $rootScope.user.cpassword = 'password'; $rootScope.$apply(); cpassword.change(); expect(cpassword.val()).not.toEqual(password.val()); }); it('should match if both passwords are the same', function () { $rootScope.user.password = 'password'; $rootScope.user.cpassword = 'password'; $rootScope.$apply(); cpassword.change(); expect(cpassword.val()).toEqual(password.val()); }); it('should not match if both passwords are different', function () { $rootScope.user.password = 'password123'; $rootScope.user.cpassword = 'password345'; $rootScope.$apply(); cpassword.change(); expect(cpassword.val()).not.toEqual(password.val()); }); }); })(); horizon-9.0.0/horizon/static/framework/util/validators/validate-number-min.directive.js0000664000567000056710000000523212701407063032627 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc directive * @name horizon.framework.util.validators.directive:validateNumberMin * @element ng-model * @description * The `validateNumberMin` directive provides min validation * for form input elements. This is an alternative to `min` * which doesn't re-evaluate the expression passed in on * change. This allows the min value to be dynamically * specified. * * The model and view value is not set to undefined if * input does not pass validation. This is so that * components that are watching this value can determine * what to do with it. For example, quota charts can * still render and display over-utilized slices in red. * * Validator returns true is model/view value >= min value. * * @restrict A * * @example * ``` * * ``` */ angular .module('horizon.framework.util.validators') .directive('validateNumberMin', validateNumberMin); function validateNumberMin() { var directive = { require: 'ngModel', restrict: 'A', link: link }; return directive; ////////// function link(scope, element, attrs, ctrl) { if (!ctrl) { return; } /** * Re-validate if value is changed through the UI * or model (programmatically) */ ctrl.$parsers.push(minValidator); ctrl.$formatters.push(minValidator); attrs.$observe('validateNumberMin', function () { minValidator(ctrl.$modelValue); }); function minValidator(value) { var min = scope.$eval(attrs.validateNumberMin); if (angular.isDefined(min) && !ctrl.$isEmpty(value) && value < min) { ctrl.$setValidity('validateNumberMin', false); } else { ctrl.$setValidity('validateNumberMin', true); } // Return the value rather than undefined if invalid return value; } } // end of link } // end of validateNumberMax })(); horizon-9.0.0/horizon/static/framework/util/validators/hz-password-match.directive.js0000664000567000056710000000444612701407063032350 0ustar jenkinsjenkins00000000000000/** * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * Copyright 2015 IBM Corp. * * 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'; /** * @ngdoc directive * @name hzPasswordMatch * @element ng-model * * @description * A directive to ensure that password matches. * Changing the password or confirmation password triggers a validation check. * The goal is to check that confirmation password matches the password. * * @restrict A * * @scope * hzPasswordMatch - id of element to validate against * * @example: *
* * *
* * Note that id and name are required for the password input. * This directive uses the form model and id for validation check. */ angular .module('horizon.framework.util.validators') .directive('hzPasswordMatch', hzPasswordMatch); function hzPasswordMatch() { var directive = { restrict: 'A', require: 'ngModel', link: link }; return directive; /////////// function link(scope, element, attr, ctrl) { /** * this ensures that typing in either input * will trigger the password match */ var pwElement = angular.element('#' + attr.hzPasswordMatch); pwElement.on('keyup change', passwordCheck); element.on('keyup change', passwordCheck); // helper function to check that password matches function passwordCheck() { scope.$apply(function () { var match = (ctrl.$modelValue === pwElement.val()); ctrl.$setValidity('match', match); }); } } // end of link } // end of directive })(); horizon-9.0.0/horizon/static/framework/util/validators/validate-number-max.directive.js0000664000567000056710000000523212701407063032631 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc directive * @name horizon.framework.util.validators.directive:validateNumberMax * @element ng-model * @description * The `validateNumberMax` directive provides max validation * for the form input elements. This is an alternative to * `max` which doesn't re-evaluate expression passed in on * change. This allows the max value to be dynamically * specified. * * The model and view value is not set to undefined if * input does not pass validation. This is so that * components that are watching this value can determine * what to do with it. For example, quota charts can * still render and display over-utilized slices in red. * * Validator returns true if model/view value <= max value. * * @restrict A * * @example * ``` * * ``` */ angular .module('horizon.framework.util.validators') .directive('validateNumberMax', validateNumberMax); function validateNumberMax() { var directive = { require: 'ngModel', restrict: 'A', link: link }; return directive; ////////// function link(scope, element, attrs, ctrl) { if (!ctrl) { return; } /** * Re-validate if value is changed through the UI * or model (programmatically) */ ctrl.$parsers.push(maxValidator); ctrl.$formatters.push(maxValidator); attrs.$observe('validateNumberMax', function () { maxValidator(ctrl.$modelValue); }); function maxValidator(value) { var max = scope.$eval(attrs.validateNumberMax); if (angular.isDefined(max) && !ctrl.$isEmpty(value) && value > max) { ctrl.$setValidity('validateNumberMax', false); } else { ctrl.$setValidity('validateNumberMax', true); } // Return the value rather than undefined if invalid return value; } } // end of link } // end of validateNumberMax })(); horizon-9.0.0/horizon/static/framework/util/file/0000775000567000056710000000000012701407231023216 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/file/file-reader.service.spec.js0000664000567000056710000000325012701407063030326 0ustar jenkinsjenkins00000000000000/* * 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'; describe('fileReader service', function() { var $scope, fileReader; beforeEach(module('horizon.framework.util.file')); beforeEach(inject(function($injector) { fileReader = $injector.get('horizon.framework.util.file.file-reader'); $scope = $injector.get('$rootScope'); })); it('should return a promise that resolves to a string', function(done) { var fileReaderStub = {}; var arrayBuffer = str2ab('file contents'); fileReaderStub.readAsArrayBuffer = function() {}; var filePromise = fileReader.readTextFile('file', fileReaderStub); filePromise.then(function(contents) { expect(contents).toEqual('file contents'); done(); }); fileReaderStub.onloadend({ target: { result: arrayBuffer } }); $scope.$apply(); }); }); function str2ab(str) { var buf = new ArrayBuffer(str.length); // 2 bytes for each char var bufView = new Uint8Array(buf); for (var i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; } })(); horizon-9.0.0/horizon/static/framework/util/file/file.module.js0000664000567000056710000000132612701407063025764 0ustar jenkinsjenkins00000000000000/* * Copyright 2015 Hewlett Packard Enterprise Development Company LP * * 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('horizon.framework.util.file', []); })(); horizon-9.0.0/horizon/static/framework/util/file/file-reader.service.js0000664000567000056710000000352312701407063027400 0ustar jenkinsjenkins00000000000000/* * Copyright 2015 Hewlett Packard Enterprise Development Company LP * * 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('horizon.framework.util.file') .factory('horizon.framework.util.file.file-reader', fileReaderService); fileReaderService.$inject = ['$q']; /** * @ngdoc service * @name horizon.framework.util.file.fileReaderService * @description * Service for reading file contents. Used for processing client-side * files, such as loading a config file into launch instance. * * readTextFile - reads a text file and returns a promise of its contents. */ function fileReaderService($q) { var service = { readTextFile: readTextFile }; return service; //////////////// function readTextFile(file, fileReader) { var reader = fileReader || new FileReader(); var deferred = $q.defer(); reader.onloadend = loadEnd; reader.readAsArrayBuffer(file.slice(0, file.size)); return deferred.promise; function loadEnd(e) { var charArray = new Uint8Array(e.target.result); var textContent = [].map.call(charArray, strFromCharCode).join(''); deferred.resolve(textContent); function strFromCharCode(char) { return String.fromCharCode(char); } } } } })(); horizon-9.0.0/horizon/static/framework/util/bind-scope/0000775000567000056710000000000012701407231024322 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/bind-scope/bind-scope.directive.js0000664000567000056710000000337112701407063030667 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc directive * @name horizon.framework.util.bind-scope.directive:bindScope * @element ng-repeat * @description * The `bindScope` directive injects the scope where it is * instantiated into the transclusion function so that the * transcluded content is rendered correctly. The content * is then append to the element where 'bind-scope-target' * is defined. * * @restrict A * * @example * ``` * * * * * * ``` */ angular .module('horizon.framework.util.bind-scope') .directive('bindScope', bindScope); function bindScope() { var directive = { restrict: 'A', link: link }; return directive; ////////// function link(scope, element, attrs, ctrl, transclude) { if (transclude) { transclude(scope, function (clone) { var detailElt = element.find('[bind-scope-target]'); if (detailElt.length) { detailElt.append(clone); } }); } } } })(); horizon-9.0.0/horizon/static/framework/util/bind-scope/bind-scope.spec.js0000664000567000056710000000362412701407063027644 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; describe('horizon.framework.util.bind-scope module', function () { it('should have been defined', function () { expect(angular.module('horizon.framework.util.bind-scope')).toBeDefined(); }); }); describe('bind-scope directive', function () { var $scope, $element; beforeEach(module('horizon.framework')); beforeEach(module('horizon.framework.util.bind-scope', function ($compileProvider) { /* eslint-disable angular/module-getter */ $compileProvider.directive('testBindScope', testBindScope); /* eslint-enable angular/module-getter */ function testBindScope() { var directive = { restrict: 'E', scope: { itemList: '=' }, transclude: true, template: '
  • ' + ' ' + '
' }; return directive; } })); beforeEach(inject(function ($injector) { var $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); $scope.fakeData = [ { id: '1', animal: 'cat' }, { id: '2', animal: 'dog' }, { id: '3', animal: 'fish' } ]; var markup = '{$ item.animal $}'; $element = angular.element(markup); $compile($element)($scope); $scope.$apply(); })); it('should have 3 list items', function () { expect($element.find('li').length).toBe(3); }); it('should have 3 list items with values "cat", "dog" and "fish"', function () { var listItems = $element.find('li'); expect(listItems[0].textContent.trim()).toBe('cat'); expect(listItems[1].textContent.trim()).toBe('dog'); expect(listItems[2].textContent.trim()).toBe('fish'); }); }); })(); horizon-9.0.0/horizon/static/framework/util/bind-scope/bind-scope.module.js0000664000567000056710000000252212701407063030173 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc overview * @name horizon.framework.util.bind-scope * @description * * # horizon.framework.util.bind-scope * * This utility widget supports binding the scope where the directive is * instantiated with the transcluded content. This is useful when trying * to display transcluded content using the `ngRepeat` scope. * * | Directives | * |--------------------------------------------------------------------------| * | {@link horizon.framework.util.bind-scope.directive:bindScope `bindScope`} | * */ angular .module('horizon.framework.util.bind-scope', []); })(); horizon-9.0.0/horizon/static/framework/util/http/0000775000567000056710000000000012701407231023256 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/http/http.js0000664000567000056710000000374612701407063024610 0ustar jenkinsjenkins00000000000000/* Copyright 2014, Rackspace, US, Inc. 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'; /* eslint-disable angular/no-service-method */ angular .module('horizon.framework.util.http', []) .service('horizon.framework.util.http.service', ApiService); /* eslint-enable angular/no-service-method */ ApiService.$inject = ['$http', '$window']; function ApiService($http, $window) { var httpCall = function (method, url, data, config) { /* eslint-disable angular/window-service */ url = $window.WEBROOT + url; /* eslint-enable angular/window-service */ url = url.replace(/\/+/g, '/'); if (angular.isUndefined(config)) { config = {}; } // url and method are always provided config.method = method; config.url = url; if (angular.isDefined(data)) { config.data = data; } return $http(config); }; this.get = function(url, config) { return httpCall('GET', url, null, config); }; this.post = function(url, data, config) { return httpCall('POST', url, data, config); }; this.patch = function(url, data, config) { return httpCall('PATCH', url, data, config); }; this.put = function(url, data, config) { return httpCall('PUT', url, data, config); }; // NOTE the deviation from $http.delete which does not have the data param this.delete = function (url, data, config) { return httpCall('DELETE', url, data, config); }; } }()); horizon-9.0.0/horizon/static/framework/util/http/http.spec.js0000664000567000056710000000530012701407063025525 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; describe('horizon.framework.util.http module', function() { it('should have been defined', function () { expect(angular.module('horizon.framework.util.http')).toBeDefined(); }); }); describe('api service', function () { var api, $httpBackend; beforeEach(module('horizon.framework')); beforeEach(inject(function ($injector) { api = $injector.get('horizon.framework.util.http.service'); $httpBackend = $injector.get('$httpBackend'); })); afterEach(function () { $httpBackend.verifyNoOutstandingExpectation(); }); it('should be defined', function () { expect(!!api).toBe(true); }); function testGoodCall(apiMethod, verb) { var called = {}; var suppliedData = verb === 'GET' ? undefined : 'some complicated data'; $httpBackend.when(verb, '/good', suppliedData).respond({status: 'good'}); $httpBackend.expect(verb, '/good', suppliedData); apiMethod('/good', suppliedData).success(function (data) { called.data = data; }); $httpBackend.flush(); expect(called.data.status).toBe('good'); } function testBadCall(apiMethod, verb) { var called = {}; var suppliedData = verb === 'GET' ? undefined : 'some complicated data'; $httpBackend.when(verb, '/bad', suppliedData).respond(500, ''); $httpBackend.expect(verb, '/bad', suppliedData); apiMethod('/bad', suppliedData).error(function () { called.called = true; }); $httpBackend.flush(); expect(called.called).toBe(true); } it('should call success on a good GET response', function () { testGoodCall(api.get, 'GET'); }); it('should call error on a bad GET response', function () { testBadCall(api.get, 'GET'); }); it('should call success on a good POST response', function () { testGoodCall(api.post, 'POST'); }); it('should call error on a bad POST response', function () { testBadCall(api.post, 'POST'); }); it('should call success on a good PATCH response', function () { testGoodCall(api.patch, 'PATCH'); }); it('should call error on a bad PATCH response', function () { testBadCall(api.patch, 'PATCH'); }); it('should call success on a good PUT response', function () { testGoodCall(api.put, 'PUT'); }); it('should call error on a bad PUT response', function () { testBadCall(api.put, 'PUT'); }); it('should call success on a good DELETE response', function () { testGoodCall(api.delete, 'DELETE'); }); it('should call error on a bad DELETE response', function () { testBadCall(api.delete, 'DELETE'); }); }); }()); horizon-9.0.0/horizon/static/framework/util/util.module.js0000664000567000056710000000143712701407063025106 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; angular .module('horizon.framework.util', [ 'horizon.framework.util.bind-scope', 'horizon.framework.util.file', 'horizon.framework.util.filters', 'horizon.framework.util.http', 'horizon.framework.util.i18n', 'horizon.framework.util.promise-toggle', 'horizon.framework.util.q', 'horizon.framework.util.tech-debt', 'horizon.framework.util.workflow', 'horizon.framework.util.validators', 'horizon.framework.util.extensible' ]) .config(config); config.$inject = ['$provide', '$windowProvider']; function config($provide, $windowProvider) { var path = $windowProvider.$get().STATIC_URL + 'framework/util/'; $provide.constant('horizon.framework.util.basePath', path); } })(); horizon-9.0.0/horizon/static/framework/util/workflow/0000775000567000056710000000000012701407231024151 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/workflow/workflow.service.js0000664000567000056710000000566212701407063030034 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * (c) Copyright 2015 ThoughtWorks, Inc. * Copyright 2015 IBM Corp. * * 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('horizon.framework.util.workflow') .factory('horizon.framework.util.workflow.service', workflowService); workflowService.$inject = [ 'horizon.framework.util.extensible.service' ]; /** * @ngdoc factory * @name horizon.framework.util.workflow.factory:workflow * @module horizon.framework.util.workflow * @kind function * @description * * Decorate the workflow specification object with specified decorators. * * @param {Object} The input workflow specification object * @param {Array} decorators A list a decorator functions. * * @returns {Object} The decorated workflow specification object, the same * reference to the input spec object. * ```js angular .module('MyModule') .factory('myService', myService); myService.$inject = ['$q', 'horizon.framework.util.workflow.service']; function myService ($q, workflow) { // a workflow specification object: var spec = { steps: [ { requireSomeServices: true }, { }, { requireSomeServices: true } ] }; // define some decorators var decorators = [ // a decorator function (spec) { var steps = spec.steps; angular.forEach(steps, function (step) { if (step.requireSomeServices) { step.checkReadiness = function () { var d = $q.defer(); // checking if the service is available asynchronously . setTimeout(function () { d.resolve(); }, 500); return d.promise; }; } }); }, // another decorator function (spec) { //... } ]; return workflow(spec, decorators); } ``` */ function workflowService(extensibleService) { return function createWorkflow(spec, decorators) { angular.forEach(decorators, function decorate(decorator) { decorator(spec); }); extensibleService(spec, spec.steps); return spec; }; } })(); horizon-9.0.0/horizon/static/framework/util/workflow/workflow.module.js0000664000567000056710000000310012701407063027642 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * (c) Copyright 2015 ThoughtWorks, Inc. * * 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'; /** * @ngdoc overview * @name horizon.framework.util.workflow * @description * * # horizon.framework.util.workflow * * This module provides utility function service `workflow` to allow * decorating a workflow object. A user (developer) friendly workflow * specification object can be shaped into a format that is friendly to * {@link horizon.framework.widgets.wizard `wizard`} by utilizing this service. * * The service provides a mechanism of decoupling general wizard UI component * from business components. * * | Factories | * |--------------------------------------------------------------------------| * | {@link horizon.framework.util.workflow.factory:workflow `workflow`} | * */ angular .module('horizon.framework.util.workflow', []); })(); horizon-9.0.0/horizon/static/framework/util/workflow/workflow.spec.js0000664000567000056710000000662712701407063027330 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; describe('horizon.framework.util.workflow module', function () { it('should have been defined', function () { expect(angular.module('horizon.framework.util.workflow')).toBeDefined(); }); }); describe('workflow factory', function () { var workflowService, spec; var decorators = [ function (spec) { angular.forEach(spec.steps, function (step) { if (step.requireSomeServices) { step.checkReadiness = function () {}; } }); } ]; beforeEach(module('horizon.framework')); beforeEach(inject(function ($injector) { workflowService = $injector.get('horizon.framework.util.workflow.service'); spec = { steps: [ { id: 'base_step_1', requireSomeServices: true }, { id: 'base_step_2' }, { id: 'base_step_3', requireSomeServices: true } ] }; })); it('workflowService is defined', function () { expect(workflowService).toBeDefined(); }); it('workflowService is a function', function () { expect(angular.isFunction(workflowService)).toBe(true); }); it('can be decorated', function () { workflowService(spec, decorators); var steps = spec.steps; expect(steps[0].checkReadiness).toBeDefined(); expect(angular.isFunction(steps[0].checkReadiness)).toBe(true); expect(steps[1].checkReadiness).not.toBeDefined(); expect(steps[2].checkReadiness).toBeDefined(); expect(angular.isFunction(steps[2].checkReadiness)).toBe(true); }); it('can be customized', function () { var workflow = workflowService(spec, decorators); expect(workflow.steps.length).toBe(3); expect(workflow.append).toBeDefined(); expect(workflow.prepend).toBeDefined(); expect(workflow.after).toBeDefined(); expect(workflow.replace).toBeDefined(); expect(workflow.remove).toBeDefined(); expect(workflow.controllers).toBeDefined(); expect(workflow.addController).toBeDefined(); expect(workflow.initControllers).toBeDefined(); expect(workflow.controllers.length).toBe(0); var last = { id: 'last' }; workflow.append(last); expect(workflow.steps.length).toBe(4); expect(workflow.steps[3]).toBe(last); var first = { id: 'first' }; workflow.prepend(first); expect(workflow.steps.length).toBe(5); expect(workflow.steps[0]).toBe(first); expect(workflow.steps[4]).toBe(last); var after = { id: 'after' }; workflow.after('base_step_2', after); expect(workflow.steps.length).toBe(6); expect(workflow.steps[0]).toBe(first); expect(workflow.steps[3]).toBe(after); expect(workflow.steps[5]).toBe(last); var replace = { id: 'replace' }; workflow.replace('base_step_1', replace); expect(workflow.steps.length).toBe(6); expect(workflow.steps[0]).toBe(first); expect(workflow.steps[1]).toBe(replace); expect(workflow.steps[3]).toBe(after); expect(workflow.steps[5]).toBe(last); workflow.remove('base_step_2'); expect(workflow.steps.length).toBe(5); expect(workflow.steps[0]).toBe(first); expect(workflow.steps[1]).toBe(replace); expect(workflow.steps[2]).toBe(after); expect(workflow.steps[4]).toBe(last); workflow.addController('MyController'); expect(workflow.controllers.length).toBe(1); }); }); })(); horizon-9.0.0/horizon/static/framework/util/filters/0000775000567000056710000000000012701407231023747 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/filters/filters.spec.js0000664000567000056710000002553612701407063026724 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; describe('horizon.framework.util.filters', function () { beforeEach(module('horizon.framework')); describe('yesno', function () { var yesnoFilter; beforeEach(inject(function (_yesnoFilter_) { yesnoFilter = _yesnoFilter_; })); it('returns Yes for true', function () { expect(yesnoFilter(true)).toBe('Yes'); }); it('returns No for false', function () { expect(yesnoFilter(false)).toBe('No'); }); it('returns No for null', function () { expect(yesnoFilter(null)).toBe('No'); }); it('returns No for undefined', function () { expect(yesnoFilter(undefined)).toBe('No'); }); it('returns Yes for other truthy values', function () { expect(yesnoFilter(7)).toBe('Yes'); expect(yesnoFilter('C')).toBe('Yes'); expect(yesnoFilter('This will be truthy')).toBe('Yes'); }); it('returns No for other falsy values', function () { expect(yesnoFilter(0)).toBe('No'); expect(yesnoFilter('')).toBe('No'); }); }); describe('gb', function () { var gbFilter; beforeEach(inject(function (_gbFilter_) { gbFilter = _gbFilter_; })); it('returns given numeric value properly', function () { expect(gbFilter(12)).toBe('12 GB'); expect(gbFilter(1200)).toBe('1.17 TB'); expect(gbFilter(-12)).toBe('-12 GB'); expect(gbFilter(12.12)).toBe('12.12 GB'); }); it('returns empty string for non-numeric', function () { expect(gbFilter('humbug')).toBe(''); }); it('returns empty string for null', function () { expect(gbFilter(null)).toBe(''); }); }); describe('mb', function () { var mbFilter; beforeEach(inject(function (_mbFilter_) { mbFilter = _mbFilter_; })); it('returns given numeric value properly', function () { expect(mbFilter(12)).toBe('12 MB'); expect(mbFilter(1200)).toBe('1.17 GB'); expect(mbFilter(-12)).toBe('-12 MB'); expect(mbFilter(12.12)).toBe('12.12 MB'); }); it('returns empty string for non-numeric', function () { expect(mbFilter('humbug')).toBe(''); }); it('returns empty string for null', function () { expect(mbFilter(null)).toBe(''); }); }); describe('title', function () { var titleFilter; beforeEach(inject(function (_titleFilter_) { titleFilter = _titleFilter_; })); it('capitalizes as expected', function () { expect(titleFilter('title')).toBe('Title'); expect(titleFilter('we have several words')).toBe('We Have Several Words'); }); it('handles non-strings correctly', function () { expect(titleFilter(42)).toBe(42); expect(titleFilter(null)).toBe(null); expect(titleFilter(undefined)).toBe(undefined); }); it('does not mess up properly capitalized strings', function () { expect(titleFilter('I Love OpenStack Horizon!')).toBe('I Love OpenStack Horizon!'); }); it('handles strings beginning with numbers', function () { expect(titleFilter('3abc')).toBe('3abc'); }); }); describe('noUnderscore', function () { var noUnderscoreFilter; beforeEach(inject(function (_noUnderscoreFilter_) { noUnderscoreFilter = _noUnderscoreFilter_; })); it('replaces all underscores with spaces', function () { expect(noUnderscoreFilter('_this_is___a_lot____of_underscores__')) .toBe(' this is a lot of underscores '); }); it('returns non-string input', function () { expect(noUnderscoreFilter(null)).toBe(null); expect(noUnderscoreFilter(false)).toBe(false); expect(noUnderscoreFilter(true)).toBe(true); expect(noUnderscoreFilter('')).toBe(''); expect(noUnderscoreFilter(21)).toBe(21); }); }); describe('noValue', function () { var noValueFilter; beforeEach(inject(function (_noValueFilter_) { noValueFilter = _noValueFilter_; })); it('returns value if there is a value', function () { expect(noValueFilter('foo')).toBe('foo'); expect(noValueFilter(' foo ')).toBe(' foo '); expect(noValueFilter(true)).toBe(true); expect(noValueFilter(false)).toBe(false); var object = {}; expect(noValueFilter(object)).toBe(object); var array = []; expect(noValueFilter(array)).toBe(array); }); it('replaces undefined, null, blank with -', function () { expect(noValueFilter(null)).toBe('-'); expect(noValueFilter()).toBe('-'); expect(noValueFilter('')).toBe('-'); expect(noValueFilter(' ')).toBe('-'); }); it('replaces undefined, null, blank with provided value', function () { expect(noValueFilter(null, 'default')).toBe('default'); expect(noValueFilter(undefined, 'default')).toBe('default'); expect(noValueFilter('', 'default')).toBe('default'); expect(noValueFilter(' ', 'default')).toBe('default'); expect(noValueFilter('value', 'default')).toBe('value'); expect(noValueFilter(false, 'default')).toBe(false); }); }); describe('noName', function () { var noNameFilter; beforeEach(inject(function (_noNameFilter_) { noNameFilter = _noNameFilter_; })); it('returns value if there is a value', function () { expect(noNameFilter('foo')).toBe('foo'); expect(noNameFilter(' foo ')).toBe(' foo '); expect(noNameFilter(' ')).toBe(' '); }); it('replaces undefined, null, blank with None', function () { expect(noNameFilter(true)).toBe('None'); expect(noNameFilter(false)).toBe('None'); expect(noNameFilter(1)).toBe('None'); var object = {}; expect(noNameFilter(object)).toBe('None'); var array = []; expect(noNameFilter(array)).toBe('None'); expect(noNameFilter(null)).toBe('None'); expect(noNameFilter()).toBe('None'); expect(noNameFilter('')).toBe('None'); }); }); describe("decode", function () { var decodeFilter; beforeEach(inject(function (_decodeFilter_) { decodeFilter = _decodeFilter_; })); it("Returns value when key is present", function () { expect(decodeFilter('PRESENT', {'PRESENT': 'Here'})).toBe('Here'); }); it("Returns value when key is present and value is falsy", function () { expect(decodeFilter('PRESENT', {'PRESENT': false})).toBe(false); }); it("Returns input when key is not present", function () { expect(decodeFilter('NOT_PRESENT', {'PRESENT': 'Here'})).toBe('NOT_PRESENT'); }); }); describe('bytes', function () { var bytesFilter; beforeEach(inject(function (_bytesFilter_) { bytesFilter = _bytesFilter_; })); it('returns TB values', function () { expect(bytesFilter(1099511627776)).toBe('1.00 TB'); }); it('returns GB values', function () { expect(bytesFilter(1073741824)).toBe('1.00 GB'); }); it('returns MB values', function () { expect(bytesFilter(1048576)).toBe('1.00 MB'); }); it('returns KB values', function () { expect(bytesFilter(1024)).toBe('1.00 KB'); }); it('returns byte values', function () { expect(bytesFilter(0)).toBe('0 bytes'); expect(bytesFilter(1)).toBe('1 bytes'); expect(bytesFilter(1023)).toBe('1023 bytes'); }); it('handles non-numbers correctly', function () { expect(bytesFilter('Yo!')).toBe(''); expect(bytesFilter(null)).toBe(''); }); }); describe('itemCount', function () { it('should return translated text with item count', inject(function (itemCountFilter) { expect(itemCountFilter(null)).toBe('Displaying 0 items'); expect(itemCountFilter(undefined)).toBe('Displaying 0 items'); expect(itemCountFilter(true)).toBe('Displaying 1 item'); expect(itemCountFilter(false)).toBe('Displaying 0 items'); expect(itemCountFilter('a')).toBe('Displaying 0 items'); expect(itemCountFilter('0')).toBe('Displaying 0 items'); expect(itemCountFilter('1')).toBe('Displaying 1 item'); expect(itemCountFilter('1e1')).toBe('Displaying 10 items'); expect(itemCountFilter('1b1')).toBe('Displaying 0 items'); expect(itemCountFilter(0)).toBe('Displaying 0 items'); expect(itemCountFilter(1)).toBe('Displaying 1 item'); expect(itemCountFilter(1.2)).toBe('Displaying 1 item'); expect(itemCountFilter(1.6)).toBe('Displaying 2 items'); expect(itemCountFilter(-1)).toBe('Displaying 0 items'); expect(itemCountFilter(-1.2)).toBe('Displaying 0 items'); }) ); it('should return translated text with item count and total', inject(function (itemCountFilter) { var expectZero = [null, false, 'a', '0', 0, -1, -1.2]; expectZero.forEach(function(x) { expect(itemCountFilter(0, x)).toBe('Displaying 0 of 0 items'); }); var expectOne = [true, '1', 0.8, 1, 1.2]; expectOne.forEach(function(x) { expect(itemCountFilter(0, x)).toBe('Displaying 0 of 1 items'); }); expect(itemCountFilter(5, 20)).toBe('Displaying 5 of 20 items'); }) ); }); describe('toISO8610DateFormat', function() { var toIsoDateFilter; beforeEach(inject(function(_toIsoDateFilter_) { toIsoDateFilter = _toIsoDateFilter_; })); it('should convert to ISO-8610 from a date string', function() { var actual = toIsoDateFilter('2015-09-22T11:00:00.000'); expect(actual).toBe('2015-09-22T11:00:00.000Z'); }); it('should convert to ISO-8610 from milliseconds', function() { var actual = toIsoDateFilter(1442919600000); expect(actual).toBe('2015-09-22T11:00:00.000Z'); }); }); describe('limit', function() { var limitFilter; beforeEach(inject(function(_limitFilter_) { limitFilter = _limitFilter_; })); it('should return valid number as is', function() { var limit = limitFilter(0); expect(limit).toBe(0); }); it('should return non-numeric value as "Unlimited"', function() { var limit = limitFilter('foo'); expect(limit).toBe('Unlimited'); }); it('should return negative number as "Unlimited"', function() { var limit = limitFilter(-1); expect(limit).toBe('Unlimited'); }); it('should return negative number as custom value', function() { var limit = limitFilter(-1, 'foo'); expect(limit).toBe('foo'); }); }); }); // end of horizon.framework.util.filters })(); // end of IIFE horizon-9.0.0/horizon/static/framework/util/filters/filters.js0000664000567000056710000001644512701407063025772 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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('horizon.framework.util.filters') .filter('yesno', yesNoFilter) .filter('gb', gbFilter) .filter('mb', mbFilter) .filter('title', titleFilter) .filter('noUnderscore', noUnderscoreFilter) .filter('noValue', noValueFilter) .filter('noName', noNameFilter) .filter('decode', decodeFilter) .filter('bytes', bytesFilter) .filter('itemCount', itemCountFilter) .filter('toIsoDate', toIsoDateFilter) .filter('limit', limitFilter); /** * @ngdoc filter * @name yesno * @description * Evaluates given input for standard truthiness and returns translation * of 'Yes' and 'No' for true/false respectively. */ yesNoFilter.$inject = ['horizon.framework.util.i18n.gettext']; function yesNoFilter(gettext) { return function (input) { return input ? gettext("Yes") : gettext("No"); }; } /** * @ngdoc filter * @name gb * @description * Expects numeric value and suffixes translated 'GB' with spacing. * Returns empty string if input is not a number or is null. */ function gbFilter() { return function (input) { var tb = 1024; if (isNaN(input) || null === input) { return ''; } else if (input >= tb) { return interpolate(gettext("%s TB"), [parseFloat(Number(input / tb).toFixed(2))]); } else if (input === '') { return interpolate(gettext("0 GB")); } else { return interpolate(gettext("%s GB"), [input.toString()]); } }; } /** * @ngdoc filter * @name mb * @description * Expects numeric value and suffixes translated 'MB' with spacing. * Returns empty string if input is not a number or is null. */ function mbFilter() { return function (input) { var gb = 1024; if (isNaN(input) || null === input) { return ''; } else if (input >= gb) { return interpolate(gettext("%s GB"), [parseFloat(Number(input / gb).toFixed(2))]); } else if (input === '') { return interpolate(gettext("0 MB")); } else { return interpolate(gettext("%s MB"), [input.toString()]); } }; } /** * @ngdoc filter * @name title * @description * Capitalizes leading characters of individual words. */ function titleFilter() { return function (input) { if (!angular.isString(input)) { return input; } return input.replace(/(?:^|\s)\S/g, function (a) { return a.toUpperCase(); }); }; } /** * @ngdoc filter * @name noUnderscore * @description * Replaces all underscores with spaces. */ function noUnderscoreFilter() { return function (input) { if (!angular.isString(input)) { return input; } return input.replace(/_/g, ' '); }; } /** * @ngdoc filter * @name noValue * @description * Replaces null / undefined / empty string with translated '-' or the optional * default value provided. */ function noValueFilter() { return function (input, def) { if (input === null || angular.isUndefined(input) || (angular.isString(input) && '' === input.trim())) { return def || gettext('-'); } else { return input; } }; } /** * @ngdoc filter * @name noName * @description * Replaces null / undefined / empty string with translated 'None'. */ function noNameFilter() { return function (input) { return input && angular.isString(input) ? input : gettext('None'); }; } /** * @ngdoc filter * @name decode * @description * Returns values based on key and given mapping. If key doesn't exist * in given mapping, return key. This is useful when translations for * codes are present. */ function decodeFilter() { return function (input, mapping) { var val = mapping[input]; return angular.isDefined(val) ? val : input; }; } /** * @ngdoc filter * @name bytes * @description * Returns a human-readable approximation of the input of bytes, * converted to a useful unit of measure. Uses 1024-based notation. */ function bytesFilter() { return function (input) { var kb = 1024; var mb = kb * 1024; var gb = mb * 1024; var tb = gb * 1024; if (isNaN(input) || null === input || input < 0) { return ''; } else if (input >= tb) { return interpolate(gettext("%s TB"), [Number(input / tb).toFixed(2)]); } else if (input >= gb) { return interpolate(gettext("%s GB"), [Number(input / gb).toFixed(2)]); } else if (input >= mb) { return interpolate(gettext("%s MB"), [Number(input / mb).toFixed(2)]); } else if (input >= kb) { return interpolate(gettext("%s KB"), [Number(input / kb).toFixed(2)]); } else { return interpolate(gettext("%s bytes"), [Math.floor(input)]); } }; } /** * @ngdoc filter * @name itemCount * @description * Displays translated count in table footer. * Input should be the number shown; an optional parameter specifies how * large the total set is regardless of the number shown. */ function itemCountFilter() { function ensureNonNegative(input) { var isNumeric = (input !== null && isFinite(input)); var number = isNumeric ? Math.round(input) : 0; return (number > 0) ? number : 0; } return function (input, totalInput) { var format; var count = ensureNonNegative(input); if (angular.isUndefined(totalInput)) { format = ngettext('Displaying %s item', 'Displaying %s items', count); return interpolate(format, [count]); } else { var total = ensureNonNegative(totalInput); format = gettext('Displaying %(count)s of %(total)s items'); return interpolate(format, {count: count, total: total}, true); } }; } /** * @ngdoc filter * @name toISO8610DateFormat * @description * Converts the string date into ISO-8610 format, which adds proper UTC * timezone identifier. */ function toIsoDateFilter() { return function(input) { return new Date(input).toISOString(); }; } /** * @ngdoc filter * @name limit * @description * If input is a number greater than or equal to zero, returns the number. Otherwise * returns the optional string argument or "Unlimited". Use for number values where * anything negative has a special meaning, such as limits where -1 typically means * unlimited. */ limitFilter.$inject = ['horizon.framework.util.i18n.gettext']; function limitFilter(gettext) { return function (input, value) { return angular.isNumber(input) && input >= 0 ? input : value || gettext('Unlimited'); }; } })(); horizon-9.0.0/horizon/static/framework/util/filters/filters.module.js0000664000567000056710000000166012701407063027247 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc overview * @name horizon.framework.util.filters * @description * horizon.framework.util.filters provides common filters to be used within Horizon. * */ angular .module('horizon.framework.util.filters', ['horizon.framework.util.i18n']); })(); horizon-9.0.0/horizon/static/framework/util/i18n/0000775000567000056710000000000012701407231023056 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/util/i18n/i18n.spec.js0000664000567000056710000000370112701407063025130 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; describe('horizon.framework.util.i18n', function () { beforeEach(module('horizon.framework')); describe('gettext', function () { var factory; describe('Normal operation', function () { beforeEach(inject(function ($injector) { factory = $injector.get("horizon.framework.util.i18n.gettext"); })); it('defines the factory', function () { expect(factory).toBeDefined(); }); it('function returns what is given', function () { expect(factory("Hello")).toBe('Hello'); }); }); describe("injected window.gettext", function () { beforeEach(module(function ($provide) { var $window = { gettext: function (x) { return x.replace(/good/, 'bad'); }}; $provide.value('$window', $window); })); // Get the factory by name. beforeEach(inject(function ($injector) { factory = $injector.get("horizon.framework.util.i18n.gettext"); })); it('uses the window gettext when available', function () { // we can't spy on the window gettext due to (appropriate) // indirection. But we can make sure it was called. expect(factory("good cop")).toBe("bad cop"); }); }); }); }); })(); horizon-9.0.0/horizon/static/framework/util/i18n/i18n.js0000664000567000056710000000360612701407063024203 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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('horizon.framework.util.i18n', []) .factory('horizon.framework.util.i18n.gettext', getText); getText.$inject = ['$window']; /** * @name horizon.framework.util.i18n.gettext * @description * Provides a wrapper for translation, using the global 'gettext' * function if it is present. Provides a method that * simply returns the input if the expected global 'gettext' is * not provided. * * Ideally once gettext is no longer needed on a global scope, * the global ref can be deleted here. For now that is not possible. * Also, if future alternate means were provided, we could put that * logic here. * * This could also be done in the context of the filter, but * the approach taken here was to separate business logic * (translation) from filters, which are arguably more * presentation-oriented. */ function getText($window) { // If no global function, revert to just returning given text. var gettextFunc = $window.gettext || function (x) { return x; }; // Eventually, could delete the window gettext references here, // or provide an appropriate method. return function () { return gettextFunc.apply(this, arguments); }; } })(); horizon-9.0.0/horizon/static/framework/.eslintrc0000664000567000056710000000031412701407063023147 0ustar jenkinsjenkins00000000000000# ESLint styles for this section of horizon's codebase. It activates the John Papa # Style plugin for all javascript files in this directory or below. # Enable eslint-plugin-angular plugins: - angular horizon-9.0.0/horizon/static/framework/conf/0000775000567000056710000000000012701407231022247 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/conf/conf.js0000664000567000056710000000145512701407063023542 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; angular .module('horizon.framework.conf', []) .constant('horizon.framework.conf.spinner_options', { inline: { lines: 10, length: 5, width: 2, radius: 3, color: '#000', speed: 0.8, trail: 50, zIndex: 100 }, modal: { lines: 10, length: 15, width: 4, radius: 10, color: '#000', speed: 0.8, trail: 50 }, line_chart: { lines: 10, length: 15, width: 4, radius: 11, color: '#000', speed: 0.8, trail: 50 } }) .value('horizon.framework.conf.toastOptions', { 'delay': 3000, 'dimissible': ['alert-success', 'alert-info'] }); })(); horizon-9.0.0/horizon/static/framework/conf/resource-type-registry.service.spec.js0000664000567000056710000001473412701407063031665 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; describe('resource type service', function() { var service; beforeEach(module('horizon.framework.conf')); beforeEach(module(function($provide) { $provide.value('horizon.framework.util.extensible.service', angular.noop); })); beforeEach(inject(function($injector) { service = $injector.get('horizon.framework.conf.resource-type-registry.service'); })); it('exists', function() { expect(service).toBeDefined(); }); it('init calls initScope on item and batch actions', function() { var action = { service: { initScope: angular.noop } }; spyOn(action.service, 'initScope'); service.getResourceType('newthing').batchActions.push(action); service.initActions('newthing', { '$new': function() { return 4; }} ); expect(action.service.initScope).toHaveBeenCalledWith(4); }); it('init ignores initScope when not present', function() { var action = { service: { } }; service.getResourceType('newthing').batchActions.push(action); var returned = service.initActions('newthing', {} ); // but we got here expect(returned).toBeUndefined(); }); describe('getResourceType', function() { it('returns a new resource type object even without a config', function() { var value = service.getResourceType('something'); expect(value).toBeDefined(); }); it('returns a new resource type object', function() { var value = service.getResourceType('something', {here: "I am"}); expect(value).toBeDefined(); }); it('takes the given properties', function() { var value = service.getResourceType('something', {here: "I am"}); expect(value.here).toEqual('I am'); }); it('has an setProperty function', function() { var value = service.getResourceType('something', {here: "I am"}); expect(value.setProperty).toBeDefined(); }); it('can be called multiple times, overlaying values', function() { var value = service.getResourceType('something', {here: "I am"}); service.getResourceType('something', {another: "Thing"}); expect(value.here).toBe('I am'); expect(value.another).toBe('Thing'); }); }); describe('label', function() { var label; beforeEach(function() { var value = service.getResourceType('something', {}) .setProperty('example', {label: gettext("Example")}) .setProperty('bad_example', {}); label = value.label; }); it('returns the property name if there is no such property', function() { expect(label('not_exist')).toBe('not_exist'); }); it('returns the property name if there is no such property label', function() { expect(label('bad_example')).toBe('bad_example'); }); it('returns the nice label if there is one', function() { expect(label('example')).toBe('Example'); }); }); describe('format', function() { var format; beforeEach(function() { var value = service.getResourceType('something', {}) .setProperty('mapping', {value_mapping: {'a': 'apple', 'j': 'jacks'}}) .setProperty('func', {value_function: function(x) { return x.replace('a', 'y'); }}) .setProperty('default-func', {value_mapping: {a: 'apple'}, value_mapping_default_function: function(x) { return x.replace('i', 'a'); }}) .setProperty('multi-func', {value_function: [ function(x) { return x.replace('a', 'y'); }, function(x) { return x.replace('y', 'x'); } ]}) .setProperty('default', {value_mapping: {}, value_mapping_default_value: 'Fell Thru'}) .setProperty('bad_example', {}); format = value.format; }); it('returns the value if there is no such property', function() { expect(format('not_exist', 'apple')).toBe('apple'); }); it('returns the value if there is no mapping, function, or default', function() { expect(format('bad_example', 'apple')).toBe('apple'); }); it('returns the mapped value if there is one', function() { expect(format('mapping', 'a')).toBe('apple'); }); it('returns the function return value if there is a value', function() { expect(format('func', 'apple')).toBe('ypple'); }); it('returns the multiple function return value if there is an array', function() { expect(format('multi-func', 'apple')).toBe('xpple'); }); it('returns the default mapping value if there is no mapping or function', function() { expect(format('default', 'apple')).toBe('Fell Thru'); }); it('returns the original value if there is no matching mapping & no default', function() { expect(format('mapping', 'what')).toBe('what'); }); it('returns the value_mapping_default_function result when no matching mapping', function() { expect(format('default-func', 'missing')).toBe('massing'); }); }); describe('getName', function() { it('returns nothing if names not provided', function() { var type = service.getResourceType('something', {}); expect(type.getName(2)).toBeUndefined(); }); it('returns plural if count not provided', function() { var type = service.getResourceType('something', {names: ['Name', 'Names']}); expect(type.getName()).toBe('Names'); }); it('returns singular if given one', function() { var type = service.getResourceType('something', {names: ["Image", "Images"]}); expect(type.getName(1)).toBe('Image'); }); it('returns plural if given two', function() { var type = service.getResourceType('something', {names: ["Image", "Images"]}); expect(type.getName(2)).toBe('Images'); }); }); }); })(); horizon-9.0.0/horizon/static/framework/conf/resource-type-registry.service.js0000664000567000056710000002440312701407063030726 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett Packard Enterprise Development Company LP * * 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('horizon.framework.conf') .factory('horizon.framework.conf.resource-type-registry.service', registryService); registryService.$inject = [ 'horizon.framework.util.extensible.service' ]; /* * @ngdoc service * @name horizon.framework.conf.resource-type-registry.service * @description * This service provides a registry which allows for registration of * configurations for resource types. The purpose of these registrations * is to make it easy for modules to register a variety of common features * that are used both in display and behavior related to resource types. * Ideally the primary members of a resource type are decided on by * the community; however it is possible using a configuration to add * all kinds of members to the resource type. * Common elements in resource type configurations include things like * batch and item actions, which are associated with the resource type * via a key. The key follows the format: OS::Glance::Image. * The functions that are exposed both assist with registration and also * provide utilities relevant for their consumption. * This service uses the extensibility service to decorate the individual * containers for the actions, so they may be best manipulated via its * decorated methods. * The concept for use is to register actions with the resource types at * the Angular module level, using the .run() function. This allows * actions to be configured by any module. * Actions should not perform complex actions in their construction, for * example API calls, because as injectables their constructor is run * during injection, meaning API calls would be executed as the module * is initialized. This would mean those calls would be executed on any * Angular context initialization, such as going to the login page. Actions * should instead place such code in their initScope() functions. */ function registryService(extensibleService) { function ResourceType() { // 'properties' contains information about properties associated with // this resource type. The expectation is that the key is the 'code' // name of the property and the value conforms to the standard // described in the setProperty() function below. var properties = {}; this.setProperty = setProperty; this.getName = getName; this.label = label; this.format = format; // itemActions is a list of actions that can be executed upon a single // item. The list is made extensible so it can be added to independently. this.itemActions = []; extensibleService(this.itemActions, this.itemActions); // batchActions is a list of actions that can be executed upon multiple // items. The list is made extensible so it can be added to independently. this.batchActions = []; extensibleService(this.batchActions, this.batchActions); /** * @ngdoc function * @name setProperty * @description * Adds a property to the resource type object. Replaces any existing * definition. These calls can be chained to other ResourceType * functions. Specific information about the logic and evaluation of * the property attributes is more fully described in the * format() function. * @example ``` resourceType.setProperty("kernel_id", { label: gettext("Kernel ID") // just provides the label. // values will be shown directly }) .setproperty("disk_size", { label: gettext("disk size"), value_function: function(size) { // uses function to return interpolate("%s GiB", size); // display values. } }) .setproperty("disk_size", { label: gettext("disk size"), value_function: [ function(size) { // uses multiple return input.replace(/-/, ' '); // functions in sequence }, // to display values. function(input) { return input.toUpperCase(); } ] }) .setProperty("status", { label: gettext("Status"), value_mapping: { // uses mapping to ready: gettext("Ready"), // look up values waiting: gettext("Waiting") }, default_value: gettext("Unknown") // uses default if no match }) .setProperty("state", { label: gettext("State"), value_mapping: { // uses mapping to initial: gettext("Initial"), // look up values running: gettext("Running") }, default_function: function(input) { // uses function if no match return input.toUpperCase(); } }) ) ``` */ function setProperty(name, prop) { properties[name] = prop; return this; } /** * @ngdoc function * @name getName * @description * Given a count, returns the appropriate name (e.g. singular or plural) * @example ``` var resourceType = getResourceType('thing', { names: [gettext('Thing'), gettext('Things')] }); var singleName = resourceType.getName(1); // returns singular ``` */ function getName(count) { if (this.names) { return ngettext.apply(null, this.names.concat([count])); } } /** * @ngdoc function * @name label * @description * Returns a human-appropriate label for the given name. * @example ``` var name = resourceType.propLabel('disk_format'); // Yields 'Disk Format' ``` */ function label(name) { var prop = properties[name]; if (angular.isDefined(prop) && angular.isDefined(prop.label)) { return prop.label; } return name; } /** * @ngdoc function * @name format * @description * Returns a well-formatted value given a property name and an * associated value. * The formatting is determined by evaluating various options on the * property. * * 'value_function' provides a single or a list of functions that will * evaluate the source value as input and return output; or in the case * of multiple functions, will chain the output of one to the input of * the next. * * 'value_mapping' provides a hash where, if a matching key is found, * the value is returned. If no matching key is found, then if * 'value_mapping_default_function' is present, the value is passed * to the function and the result is returned. Finally, if there was * no matching key and no default function, 'value_mapping_default_value' * provides a string to be returned. * * If these options are not present, the original value is returned. * value. * @example ``` var value = resourceType.format('disk_size', 12); // Yields '12 GiB' ``` */ function format(name, value) { var prop = properties[name]; if (angular.isUndefined(prop)) { // no property definition; return the original value. return value; } else if (prop.value_function) { if (angular.isArray(prop.value_function)) { return prop.value_function.reduce(function execEach(prev, func) { return func(prev); }, value); } else { return prop.value_function(value); } } else if (prop.value_mapping) { if (angular.isDefined(prop.value_mapping[value])) { return prop.value_mapping[value]; } else if (angular.isDefined(prop.value_mapping_default_function)) { return prop.value_mapping_default_function(value); } else if (angular.isDefined(prop.value_mapping_default_value)) { return prop.value_mapping_default_value; } } // defaults to simply returning the original value. return value; } } var resourceTypes = {}; var registry = { getResourceType: getResourceType, initActions: initActions }; /* * @ngdoc function * @name getResourceType * @description * Retrieves all information about a resource type. If the resource * type doesn't exist in the registry, this creates a new entry. * If a configuration is supplied, the resource type is extended to * use the configuration's properties. */ function getResourceType(type, config) { if (!resourceTypes.hasOwnProperty(type)) { resourceTypes[type] = new ResourceType(); } if (angular.isDefined(config)) { angular.extend(resourceTypes[type], config); } return resourceTypes[type]; } /* * @ngdoc function * @name initActions * @description * Performs initialization (namely, scope-setting) of all actions * for the given type. This requires the proper scope be passed. * If an action does not have an initScope() function, it is ignored. */ function initActions(type, scope) { angular.forEach(resourceTypes[type].itemActions, setActionScope); angular.forEach(resourceTypes[type].batchActions, setActionScope); function setActionScope(action) { if (action.service.initScope) { action.service.initScope(scope.$new()); } } } return registry; } })(); horizon-9.0.0/horizon/static/framework/framework.module.js0000664000567000056710000000567612701407063025162 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; angular .module('horizon.framework', [ 'horizon.framework.conf', 'horizon.framework.util', 'horizon.framework.widgets' ]) .config(config) .run(run); config.$inject = [ '$injector', '$provide', '$interpolateProvider', '$httpProvider', '$windowProvider' ]; function config($injector, $provide, $interpolateProvider, $httpProvider, $windowProvider) { var path = $windowProvider.$get().STATIC_URL + 'framework/'; $provide.constant('horizon.framework.basePath', path); // Replacing the default angular symbol // allow us to mix angular with django templates $interpolateProvider.startSymbol('{$'); $interpolateProvider.endSymbol('$}'); // Http global settings for ease of use $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; $httpProvider.defaults.xsrfCookieName = 'csrftoken'; $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; $httpProvider.defaults.headers.common['Content-Type'] = 'application/json;charset=utf-8'; // NOTE(tsufiev): angular-ui/bootstrap v0.11.2 dropdownToggle directive // conflicts with the native Bootstrap data-toggle="dropdown" attribute // (see https://github.com/angular-ui/bootstrap/issues/2156). // This is fixed in 0.13, but before that it'd be valuable to ensure that // the same html markup works the same way with both versions of // angular-ui/bootstrap (0.11.2 and 0.13). Could be safely deleted once // Horizon migrates to angular-ui/bootstra v0.13 if ($injector.has('dropdownToggleDirective')) { $provide.decorator('dropdownToggleDirective', patchDropdowns); } patchDropdowns.$inject = ['$delegate']; function patchDropdowns($delegate) { var directive = $delegate[0]; directive.restrict = 'A'; return $delegate; } // Global http error handler // if user is not authorized, log user out // this can happen when session expires $httpProvider.interceptors.push(redirect); redirect.$inject = ['$q']; function redirect($q) { return { responseError: function (error) { if (error.status === 401) { $windowProvider.$get().location.replace('/auth/logout'); } return $q.reject(error); } }; } } run.$inject = ['$window', '$rootScope']; function run($window, $rootScope) { $window.recompileAngularContent = recompileAngularContent; function recompileAngularContent($element) { function explicit($compile) { // NOTE(tsufiev): recompiling elements with ng-click directive spawns // a new 'click' handler even if there were some, so we need to cleanup // existing handlers before doing this. $element.find('[ng-click]').off('click'); $compile($element)($rootScope); } explicit.$inject = ['$compile']; $element.injector().invoke(explicit); } } })(); horizon-9.0.0/horizon/static/framework/framework.scss0000664000567000056710000000003312701407063024213 0ustar jenkinsjenkins00000000000000@import "widgets/widgets"; horizon-9.0.0/horizon/static/framework/widgets/0000775000567000056710000000000012701407231022770 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/magic-search/0000775000567000056710000000000012701407231025313 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/magic-search/hz-magic-search-bar.directive.js0000664000567000056710000000766112701407063033347 0ustar jenkinsjenkins00000000000000/* * 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('horizon.framework.widgets.magic-search') .directive('hzMagicSearchBar', hzMagicSearchBar); hzMagicSearchBar.$inject = ['horizon.framework.widgets.basePath']; /** * @ngdoc directive * @name MagicSearch.directive:hzMagicSearchBar * @element * @param {object} filterFacets Facets allowed for searching * @param {object=} filterStrings Help content shown in search bar * @param {object=} clientFullTextSearch if full text search is to be done on the client * @description * The `magicSearchBar` directive provides a template for a * client side faceted search that utilizes Smart-Table's * filtering capabilities as well. * * Facets: * ``` * var nameFacet = { * label: gettext('Name'), * name: 'name', * singleton: true * }; * * var sizeFacet = { * label: gettext('Size'), * name: 'size', * singleton: false, * options: [ * { label: gettext('Small'), key: 'small' }, * { label: gettext('Medium'), key: 'medium' }, * { label: gettext('Large'), key: 'large' }, * ] * }; * * label - this is the text shown in top level facet dropdown menu * name - this is the column key provided to Smart-Table * singleton - 'true' if free text can be used as search term * options - list of options shown in selected facet dropdown menu * ``` * * @restrict E * @scope * * @example * ``` * * * * or * * * * * or * * * * ``` */ function hzMagicSearchBar(basePath) { var directive = { compile: compile, restrict: 'E', scope: { filterStrings: '=?', filterFacets: '=', clientFullTextSearch: '=?', searchSettingsCallback: '=?' }, templateUrl: basePath + 'magic-search/hz-magic-search-bar.html' }; return directive; ////////// function link(scope) { scope.clientFullTextSearch = angular.isDefined(scope.clientFullTextSearch) ? scope.clientFullTextSearch : true; // if filterStrings is not defined, set defaults var defaultFilterStrings = { cancel: gettext('Cancel'), prompt: gettext('Click here for filters.'), remove: gettext('Remove'), text: (scope.clientFullTextSearch ? gettext('Search in current results') : gettext('Full Text Search')) }; scope.filterStrings = scope.filterStrings || defaultFilterStrings; if (angular.isDefined(scope.searchSettingsCallback)) { scope.showSettings = true; } else { scope.showSettings = false; } } function compile(element) { /** * Need to set template here since MagicSearch template * attribute is not interpolated. Can't hardcode the * template location and need to use basePath. */ var templateUrl = basePath + 'magic-search/magic-search.html'; element.find('magic-search').attr('template', templateUrl); element.addClass('hz-magic-search-bar'); return link; } } })(); horizon-9.0.0/horizon/static/framework/widgets/magic-search/hz-magic-search-bar.spec.js0000664000567000056710000001162212701407063032313 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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"; describe('hz-magic-search-bar directive', function () { var $element, $scope, $compile; beforeEach(module('templates')); beforeEach(module('smart-table')); beforeEach(module('horizon.framework')); beforeEach(module('horizon.framework.widgets.magic-search')); beforeEach(inject(function ($injector) { $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); $scope.rows = []; $scope.filterStrings = { cancel: gettext('Cancel'), prompt: gettext('Prompt'), remove: gettext('Remove'), text: gettext('Text') }; $scope.filterFacets = [ { name: 'name', label: gettext('Name'), singleton: true }, { name: 'status', label: gettext('Status'), options: [ { key: 'active', label: gettext('Active') }, { key: 'shutdown', label: gettext('Shutdown') }, { key: 'error', label: gettext('Error') } ] }, { name: 'flavor', label: gettext('Flavor'), singleton: true, options: [ { key: 'm1.tiny', label: gettext('m1.tiny') }, { key: 'm1.small', label: gettext('m1.small') } ] } ]; var markup = '' + '' + ' ' + ' ' + ' ' + '' + '' + '
' + ' ' + ' ' + '
'; $element = $compile(angular.element(markup))($scope); $scope.$apply(); })); it('st-magic-search should be defined', function () { var stSearchBar = $element.find('st-magic-search'); expect(stSearchBar.length).toBe(1); }); it('magic-search should be defined', function () { var searchBar = $element.find('magic-search'); expect(searchBar.length).toBe(1); }); it('sets the clientFullTextSearch to false', function () { var markup = '' + '' + ' ' + ' ' + ' ' + '' + '' + '
' + ' ' + ' ' + '
'; var $element = $compile(angular.element(markup))($scope); $scope.$apply(); expect($element.find('magic-search').scope().clientFullTextSearch).toEqual(false); }); it('sets the clientFullTextSearch to true', function () { var markup = '' + '' + ' ' + ' ' + ' ' + '' + '' + '
' + ' ' + ' ' + '
'; var $element = $compile(angular.element(markup))($scope); $scope.$apply(); expect($element.find('magic-search').scope().clientFullTextSearch).toEqual(true); }); it('use filterStrings defaults if not provided as attribute', function () { var markup = '' + '' + ' ' + ' ' + ' ' + '' + '' + '
' + ' ' + ' ' + '
'; $element = $compile(angular.element(markup))($scope); $scope.$apply(); var filterStrings = $element.find('magic-search').attr('strings'); expect(filterStrings).toBeDefined(); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/magic-search/hz-magic-search-bar.html0000664000567000056710000000122412701407063031707 0ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/magic-search/magic-search.service.js0000664000567000056710000002624412701407063031646 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @fileOverview Magic Search JS * @requires AngularJS * * Common terminology: * Search Term - A single unit of a search as a string. This is the * minimal representation of a search. * e.g. 'status=active' * * Search Term Object - An object representation of a search term, * e.g. {field: 'status', value: 'active'} * * Facet - An object representing a unit of a search. Includes the * search term from above. A search is composed of a list of facets. * Note, this contains all the cross-referenced labels from the facet * choice. * e.g. {name: 'status=active', singleton=true, * label: ['Status', 'Active']} * * Facet Choice - An object representing a type of facet that can be used * in a search. * e.g. {name: 'status', singleton=true, options: [...]} * * Option Choice - An object representing an option for a facet choice. * e.g. {key: 'active', label: ['first', 'middle', 'last'] TODO check */ angular.module('horizon.framework.widgets.magic-search') .factory('horizon.framework.widgets.magic-search.service', magicSearchService); magicSearchService.$inject = []; /** * @ngdoc service * @name horizon.framework.widgets.magic-search.service * * @returns the service. */ function magicSearchService() { var service = { getFacetChoice: getFacetChoice, removeOptionChoice: removeOptionChoice, removeFacetChoice: removeFacetChoice, removeChoice: removeChoice, getEventCode: getEventCode, getFacet: getFacet, getSearchTermsFromQueryString: getSearchTermsFromQueryString, getFacetChoicesFromFacetsParam: getFacetChoicesFromFacetsParam, getFacetsFromSearchTerms: getFacetsFromSearchTerms, getSearchTermObject: getSearchTermObject, getMatchingFacets: getMatchingFacets, getMatchingOptions: getMatchingOptions, getName: getName, getTextFacet: getTextFacet, getUnusedFacetChoices: getUnusedFacetChoices, getQueryPattern: getQueryPattern }; return service; // The following functions are primarily used to assist with various // map/reduce/filter uses in other functions. function objectify(obj) { return Object.create(obj); } function hasOptions(item) { return angular.isDefined(item.options); } function getTextFacet(searchVal, label) { return getFacet('text', searchVal, label, searchVal); } function getFacet(field, value, typeLabel, searchLabel) { return {'name': field + '=' + value, 'label': [typeLabel, searchLabel]}; } function getSearchTermsFromQueryString(queryString) { return queryString.replace(/^\?/, '').split('&'); } function getName(obj) { return obj.name; } function getQueryPattern(searchTermList) { return searchTermList.filter(isNotTextSearch).map(getName).join('&'); function isNotTextSearch(item) { return item.name.indexOf('text') !== 0; } } function matchesName(name) { return function(facet) { return name === facet.name; }; } function matchesKey(name) { return function(option) { return name === option.key; }; } function hasLabel(item) { return angular.isDefined(item.label); } function getSearchTermObject(str) { var parts = str.split('='); return {type: parts[0], value: parts[1]}; } // Given an item with a label, returns an array of three parts if the // string is a substring of the label. Returns undefined if no match. // e.g.: 'searchforme', 'for' -> ['search', 'for', 'me'] // Used to construct labels for options and facet choices based on // search terms. // TODO: not sure where the third element is used. function itemToLabel(item, search) { var idx = item.label.toLowerCase().indexOf(search); if (idx > -1) { return [item.label.substring(0, idx), item.label.substring(idx, idx + search.length), item.label.substring(idx + search.length)]; } } // Helper function to more obviously perform the function // for the choice(s) in the facet list that match by name. // In theory there should only be one, but that's not enforceable. // The function should expect that the single parameter is the matching // choice. function execForMatchingChoice(facetChoices, name, func) { facetChoices.filter(matchesName(name)).forEach(func); } // Exposed functions function getEventCode(evt) { return evt.which || evt.keyCode || evt.charCode; } function getFacetChoice(orig) { var facetChoice = objectify(orig); // if there are options, copy their objects as well. Expects a list. if (angular.isDefined(orig.options)) { facetChoice.options = orig.options.map(objectify); } return facetChoice; } // Translates options, returning only those that actually got labels // (those that matched the search). function getMatchingOptions(list, search) { return list.map(processOption).filter(hasLabel); function processOption(option) { return {'key': option.key, 'label':itemToLabel(option, search)}; } } // Translates facets, returning only those that actually got labels // (those that matched the search). function getMatchingFacets(list, search) { return list.map(processFacet).filter(hasLabel); function processFacet(facet) { return {'name':facet.name, 'label':itemToLabel(facet, search), 'options':facet.options}; } } function getFacetChoicesFromFacetsParam(param) { if (angular.isString(param)) { // Parse facets JSON and convert to a list of facets. var tmp = param.replace(/__apos__/g, "\'") .replace(/__dquote__/g, '\\"') .replace(/__bslash__/g, "\\"); return angular.fromJson(tmp); } // Assume this is a usable javascript object return param; } // Takes in search terms in the form of field=value, ... // then returns a list of facets, complete with // labels. Basically, a merge of data from the current search // and the facet choices. function getFacetsFromSearchTerms(searchTerms, textSearch, textSearchLabel, facetChoices) { var buff = []; searchTerms.map(getSearchTermObject).forEach(getFacetFromObj); if (angular.isDefined(textSearch)) { var currentTextSearch = searchTerms.filter(function(searchField) { return searchField.indexOf(textSearch) === 0; }); if (currentTextSearch.length === 0) { buff.push(getTextFacet(textSearch, textSearchLabel)); } } return buff; function getFacetFromObj(searchTermObj) { execForMatchingChoice(facetChoices, searchTermObj.type, addFacet); function addFacet(facetChoice) { if (angular.isUndefined(facetChoice.options)) { buff.push(getFacet(searchTermObj.type, searchTermObj.value, facetChoice.label, searchTermObj.value)); } else { facetChoice.options.filter(matchesKey(searchTermObj.value)).forEach(function (option) { buff.push(getFacet(searchTermObj.type, searchTermObj.value, facetChoice.label, option.label)); }); } } } } // The rest of the functions have to do entirely with removing // facets from the choices that are presented to the user. // Retrieves Facet Choices and returns only those not used in the // given facets (those not in the current search). function getUnusedFacetChoices(facetChoices, facets) { var unused = angular.copy(facetChoices); facets.map(getSearchTermObject).forEach(processSearchTerm); return unused; function processSearchTerm(searchTerm) { // finds any/all matching choices (should only be one) execForMatchingChoice(unused, searchTerm.type, removeFoundChoice); function removeFoundChoice(choice) { if (angular.isUndefined(choice.options)) { // for refresh case, need to remove facets that were // bookmarked/current when browser refresh was clicked removeFacetChoice(searchTerm.type, unused); } else if (choice.options.some(matchesKey(searchTerm.value))) { removeSingleChoice(choice, searchTerm, unused); } } } } // remove entire facet choice function removeFacetChoice(type, target) { execForMatchingChoice(target.slice(), type, removeFacet); function removeFacet(facet) { target.splice(target.indexOf(facet), 1); } } // Removes a choice from the target, based on the values in the search // term object. If the choice is of type 'singleton', the entire // facet choice is removed; otherwise it removes the choice's option. function removeSingleChoice(facetChoice, searchTermObj, target) { if (facetChoice.singleton === true) { removeFacetChoice(searchTermObj.type, target); } else { removeOptionChoice(searchTermObj, target); } } // Removes an item from the given target list; uses src (list of the types) // so it may reference whether the type is a singleton or not. function removeChoice(searchTerm, src, target) { execForMatchingChoice(src, searchTerm.type, removeFacetOrOption); function removeFacetOrOption(facet) { removeSingleChoice(facet, searchTerm, target); } } // Removes an option from a facet choice, based on a search term object. function removeOptionChoice(searchTermObj, target) { execForMatchingChoice(target.slice().filter(hasOptions), searchTermObj.type, removeOption); function removeOption(choice) { // Slim down choices based on key match. choice.options = choice.options.filter(keyNotMatch(searchTermObj.value)); // If there are no remaining options for this choice, remove the // choice entirely. if (choice.options.length === 0) { // Manipulating a list that it's going through. // This happens to work due to how it is iterated and searched // but arguably is not ideal. We want to retain the original // array object due to references elsewhere. target.splice(target.indexOf(choice), 1); } function keyNotMatch(value) { return function keyNotMatchBool(option) { return option.key !== value; }; } } } } })(); horizon-9.0.0/horizon/static/framework/widgets/magic-search/magic-search.scss0000664000567000056710000000602612701407063030542 0ustar jenkinsjenkins00000000000000// Replaces magic_search.scss with styles for bootstrap/Horizon. /*----------------------------------------- Magic Search bar ----------------------------------------- */ // Helper variable $magic-search-padding: ceil($input-height-base - $font-size-small * $line-height-base - $padding-small-vertical * 2)/2; hz-magic-search-bar { .input-group { width: 100%; & > st-magic-search.form-control { padding: 0 $padding-xs-horizontal 0 0; min-height: $input-height-base + 2; height: 100%; } } // Specificity Required .form-group .input-group .input-group-addon { border-bottom-right-radius: 0; border-top-right-radius: 0; } .input-group-btn { // Notes: // * We may need a better solution than this if we // ever want to support button groups that are // small or large, or a state other than default. // But, until then, this does the trick for our // standard use case! // * If this seems odds, its because Bootstrap button // groups don't allow the trailing button to grow in // height so we have to make the whole span look like // the button instead. This can be confusing: the // click event needs to live on the span now, not the button. border-radius: 0 $border-radius-base $border-radius-base 0; border: 1px solid $btn-default-border; border-left: none; cursor: pointer; outline: 0; background-image: none; &:active { @include box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); } @include button-variant($btn-default-color, $btn-default-bg, $btn-default-border); button { &, &:hover { background-color: transparent; border: none; box-shadow: none; } &:active { @include box-shadow(none); } } } .search-bar { display: table; width: 100%; height: $line-height-computed; // Layout .magic-search-clear, .search-main-area { display: table-cell; } .search-main-area { padding-bottom: $magic-search-padding; } .fi-filter { display: none; } // Input Text Box .search-input { border: none; width: 250px; margin: $magic-search-padding + 2 0 0 $magic-search-padding + 2; &:focus { outline: none; } } // Token .item { font-size: $font-size-small; border-radius: $border-radius-base; display: inline-block; background-color: $gray-lighter; padding: $padding-small-vertical 0 $padding-small-vertical $padding-small-horizontal; margin: $magic-search-padding 0 0 $padding-xs-horizontal; .remove { padding: 0 $padding-small-vertical; color: $brand-danger; } } // Pre-Token Label .search-selected { color: $gray-light; padding-left: $padding-small-vertical; } // Search Bar Clear Icon .magic-search-clear { font-size: $font-size-base; color: $gray; width: $font-size-base; vertical-align: middle; text-align: center; } } } horizon-9.0.0/horizon/static/framework/widgets/magic-search/magic-search.service.spec.js0000664000567000056710000003061712701407063032576 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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"; describe('magic-search service', function () { var service; beforeEach(module("horizon.framework.widgets.magic-search")); beforeEach(inject(function($injector) { service = $injector.get('horizon.framework.widgets.magic-search.service'); })); it('should have been defined', function () { expect(service).toBeDefined(); }); describe("getFacet", function() { it("produces the expected output", function() { expect(service.getFacet('abc', 'def', 'type-label', 'option-label')) .toEqual({'name': 'abc=def', 'label': ['type-label', 'option-label']}); }); }); describe("getTextFacet", function() { it("produces the expected output", function() { expect(service.getTextFacet('abc', 'type-label')) .toEqual({'name': 'text=abc', 'label': ['type-label', 'abc']}); }); }); describe('getEventCode', function() { it("looks in evt.which", function() { var evt = {which: 42}; expect(service.getEventCode(evt)).toBe(42); }); it("looks in evt.keyCode", function() { var evt = {keyCode: 42}; expect(service.getEventCode(evt)).toBe(42); }); it("looks in evt.charCode", function() { var evt = {charCode: 42}; expect(service.getEventCode(evt)).toBe(42); }); it("returns undefined if no code set", function() { var evt = {}; expect(service.getEventCode(evt)).toBeUndefined(); }); }); describe('getFacetChoice', function() { it("copies facets", function() { var input = {a: 'apple'}; var output = service.getFacetChoice(input); expect(output.a).toBe('apple'); }); it("copies facets' options", function() { var input = {a: 'apple', options: [{b: 'badwolf'}]}; var output = service.getFacetChoice(input); expect(output.a).toBe('apple'); expect(output.options[0].b).toBe('badwolf'); }); }); describe('getQueryPattern', function() { it("returns an empty query if nothing in input", function() { expect(service.getQueryPattern([])).toBe(''); }); it("returns proper values", function() { expect(service.getQueryPattern([{name: 'mytext'}])).toBe('mytext'); expect(service.getQueryPattern([{name: 'nothing'}, {name: 'mytext'}])) .toBe('nothing&mytext'); }); it("doesn't process items with name starting with 'text'", function() { expect(service.getQueryPattern([{name: 'nothing'}, {name: 'mytext'}, {name: 'textbad'}])).toBe('nothing&mytext'); }); }); describe('removeFacetChoice', function() { it("removes items with the given name", function() { var target = [{name: 'me'}, {name: 'me'}, {name: 'notme'}]; var remove = "me"; service.removeFacetChoice(remove, target); expect(target).toEqual([{name: 'notme'}]); }); }); describe("getMatchingOptions", function() { it("filters properly", function() { var list = [{key: "wallie", label: "findwaldonow"}, {label: "monster"}]; var search = "waldo"; var result = service.getMatchingOptions(list, search); expect(result).toEqual([{key: 'wallie', label: ['find', 'waldo', 'now']}]); }); }); describe("getFacetChoicesFromFacetsParam", function() { it("returns any object passed if not a string", function() { expect(service.getFacetChoicesFromFacetsParam({my: "thing"})).toEqual({my: "thing"}); }); it("processes a basic JSON string", function() { expect(service.getFacetChoicesFromFacetsParam('{"my": "thing"}')).toEqual({my: "thing"}); }); it("processes a JSON strings, translating characters", function() { expect(service.getFacetChoicesFromFacetsParam('{"my": "\\\\thing\'s"}')) .toEqual({my: "\\thing's"}); }); }); describe("getSearchTermsFromQueryString", function() { it("returns split of values if no leading question mark", function() { var input = "this&is&amazing"; expect(service.getSearchTermsFromQueryString(input)).toEqual(['this', 'is', 'amazing']); }); it("returns split of values if leading question mark", function() { var input = "?this&is&amazing"; expect(service.getSearchTermsFromQueryString(input)).toEqual(['this', 'is', 'amazing']); }); }); describe("getName", function() { it("extracts the name", function() { expect(service.getName({name: 'Joe'})).toEqual('Joe'); }); }); describe("getFacetsFromSearchTerms", function() { var types, searchTerm; beforeEach(function() { types = [{name: 'a', label: 'Apple'}, {name: 'b'}, {name: 'c'}]; }); it("returns nothing if given nothing", function() { expect(service.getFacetsFromSearchTerms([], searchTerm, 'txt', types)).toEqual([]); }); it("returns nothing if nothing matching", function() { var input = ["z=zebra"]; expect(service.getFacetsFromSearchTerms(input, searchTerm, 'txt', types)).toEqual([]); }); it("returns proper facet if given a match", function() { var input = ["a=apple"]; expect(service.getFacetsFromSearchTerms(input, searchTerm, 'txt', types)) .toEqual([{name: 'a=apple', label:['Apple','apple']}]); }); it("returns proper facet if given a match with options", function() { var input = ["a=gala"]; types[0].options = [{key: 'gala', label: 'Gala'},{key: 'honeycrisp', label: 'Honeycrisp'}]; expect(service.getFacetsFromSearchTerms(input, searchTerm, 'txt', types)) .toEqual([{name: 'a=gala', label:['Apple','Gala']}]); }); it("appends textSearch facet if given a match and a textSearch", function() { var input = ["a=apple"]; searchTerm = 'searchme'; expect(service.getFacetsFromSearchTerms(input, searchTerm, 'txt', types)) .toEqual([{name: 'a=apple', label:['Apple','apple']}, {name: 'text=searchme', label: ['txt', 'searchme']}]); }); }); describe("getMatchingFacets", function() { it("filters properly", function() { var list = [{name: "wallie", label: "findwaldonow", options: []}, {label: "monster"}]; var search = "waldo"; var result = service.getMatchingFacets(list, search); expect(result).toEqual([{name: 'wallie', label: ['find', 'waldo', 'now'], options: []}]); }); }); describe("removeChoice", function() { it("deletes the singletons", function() { var facet = {name: "deleteme", singleton: true, options: []}; var src = [facet, {name: "ok"}]; var target = [facet, {name: "can't get me"}]; var name = {type: "deleteme", value: {}}; service.removeChoice(name, src, target); expect(target).toEqual([{name: "can't get me"}]); }); it("deletes the non-singletons", function() { var facet = {name: "deleteme", singleton: false, options: []}; var src = [facet]; var target = [facet]; var name = {type: "deleteme", value: {}}; service.removeChoice(name, src, target); expect(target).toEqual([]); }); }); describe('removeOptionChoice', function() { it("removes nothing if no matches", function() { var facets = [{name: 'one', options: [{}]}, {name: 'two'}]; var facetParts = ['nomatch', {}]; service.removeOptionChoice(facetParts, facets); expect(facets).toEqual([{name: 'one', options: [{}]}, {name: 'two'}]); }); it("removes nothing if matches but no options", function() { var facets = [{name: 'one', options: [{}]}, {name: 'two'}]; var facetParts = ['two', {}]; service.removeOptionChoice(facetParts, facets); expect(facets).toEqual([{name: 'one', options: [{}]}, {name: 'two'}]); }); it("removes item if matches and has empty options", function() { var facets = [{name: 'one', options: []}, {name: 'two'}]; var remove = {type: 'one', value: {}}; service.removeOptionChoice(remove, facets); expect(facets).toEqual([{name: 'two'}]); }); it("removes option if key matches but doesn't remove item if options remain", function() { var facets = [{name: 'one', options: [{key: 'keymatch'}, {key: 'notmatch'},{key: 'another'}]}, {name: 'two'}]; var remove = {type: 'one', value: 'keymatch'}; service.removeOptionChoice(remove, facets); expect(facets).toEqual([{name: 'one', options: [{key: 'notmatch'}, {key: 'another'}]}, {name: 'two'}]); }); it("removes all options if key matches", function() { var facets = [{name: 'one', options: [{key: 'keymatch'}, {key: 'keymatch'},{key: 'another'}]}, {name: 'two'}]; var remove = {type: 'one', value: 'keymatch'}; service.removeOptionChoice(remove, facets); expect(facets).toEqual([{name: 'one', options: [{key: 'another'}]}, {name: 'two'}]); }); it("removes all facets if name matches", function() { var facets = [{name: 'one', options: []}, {name: 'one', options: []}, {name: 'two'}]; var remove = {type: 'one', value: {}}; service.removeOptionChoice(remove, facets); expect(facets).toEqual([{name: 'two'}]); }); it("does nothing if no options defined for a facet", function() { var facets = [{name: 'one', options: []}, {name: 'one'}, {name: 'two'}]; var remove = {type: 'one', value: {}}; service.removeOptionChoice(remove, facets); expect(facets).toEqual([{name: 'one'}, {name: 'two'}]); }); }); describe("getUnusedFacetChoices", function() { it("does nothing with unmatched facet name", function() { var facets = ["one=thing", "two=thing"]; var choices = [{name: "something"}]; var unused = service.getUnusedFacetChoices(choices, facets); expect(unused).toEqual([{name: "something"}]); }); it("removes facet with matched facet name and no options", function() { var facets = ["something=thing", "two=thing"]; var choices = [{name: "something"}]; var unused = service.getUnusedFacetChoices(choices, facets); expect(unused).toEqual([]); }); it("removes facet with matched facet name and options", function() { var facets = ["something=thing", "two=thing"]; var choices = [{name: "something", options: [{key: 'thing'}]}, {name: "something", options: [{key: 'other'}]},{name: "other"}]; var unused = service.getUnusedFacetChoices(choices, facets); expect(unused).toEqual([ {name: "something", options: [{key: 'other'}]}, {name: "other"}]); }); it("removes option with matched facet name and options", function() { var facets = ["something=thing", "two=thing"]; var choices = [{name: "something", options: [{key: 'thing'}, {key: 'other'}]}, {name: "something", options: [{key: 'other'}]},{name: "other"}]; var unused = service.getUnusedFacetChoices(choices, facets); expect(unused).toEqual([ {name: "something", options: [{key: 'other'}]}, {name: "something", options: [{key: 'other'}]}, {name: "other"}]); }); it("removes facet with matched facet name and options if a singleton", function() { var facets = ["something=thing", "another=thing"]; var choices = [{name: "something", singleton: true, options: [{key: 'thing'}, {key: 'other'}]}, {name: "another", options: [{key: 'thing'}, {key: 'more'}]},{name: "other"}]; var unused = service.getUnusedFacetChoices(choices, facets); expect(unused).toEqual([ {name: "another", options: [{key: 'more'}]}, {name: "other"}]); }); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/magic-search/st-magic-search.directive.js0000664000567000056710000001246312701407063032606 0ustar jenkinsjenkins00000000000000/* * 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('horizon.framework.widgets.magic-search') .directive('stMagicSearch', stMagicSearch); stMagicSearch.$inject = ['$timeout']; /** * @ngdoc directive * @name MagicSearch.directive:stMagicSearch * @element * @description * A directive to make Magic Search be a replacement for st-search. * This directive must be outside of a magic-search and inside a * smart-table. This lets MS drive the same filtering capabilities * in smart-table that st-search does, including filtering on all * columns or a specific column (e.g. a facet filters a column). * * @restrict E * @scope * * @example * ``` * * * * * ``` */ function stMagicSearch($timeout) { var directive = { link: link, require: '^stTable', restrict: 'E', scope: true }; return directive; function link(scope, element, attr, tableCtrl) { var clientFullTextSearch = (angular.isDefined(scope.clientFullTextSearch) ? scope.clientFullTextSearch : true); scope.currentServerSearchParams = {}; // Generate predicate object from dot notation string function setPredObj(predicates, predObj, input) { var lastPred = predicates.pop(); angular.forEach(predicates, function(pred) { predObj = predObj[pred] = {}; }); predObj[lastPred] = input; } function setServerFacetSearch(scope, query) { var currentServerSearchParams = angular.copy(scope.currentServerSearchParams); currentServerSearchParams.magicSearchQuery = query; checkAndEmit(scope, currentServerSearchParams); } function setServerTextSearch(scope, text) { var currentServerSearchParams = angular.copy(scope.currentServerSearchParams); currentServerSearchParams.queryString = text; checkAndEmit(scope, currentServerSearchParams); } function checkAndEmit(scope, serverSearchParams) { if (serverSearchParams !== scope.currentServerSearchParams) { serverSearchParams.magicSearchQueryChanged = !angular.equals(scope.currentServerSearchParams.magicSearchQuery, serverSearchParams.magicSearchQuery); serverSearchParams.queryStringChanged = !angular.equals(scope.currentServerSearchParams.queryString, serverSearchParams.queryString); scope.currentServerSearchParams = serverSearchParams; if (serverSearchParams.queryStringChanged || serverSearchParams.magicSearchQueryChanged) { scope.$emit('serverSearchUpdated', angular.copy(scope.currentServerSearchParams)); } } } // When user types a character, search the table var textSearchWatcher = scope.$on('textSearch', function(event, text) { // Timeout needed to prevent // $apply already in progress error $timeout(function() { if (clientFullTextSearch) { tableCtrl.search(text); } else { setServerTextSearch(scope, text); } }); }); // When user changes a facet, use API filter var searchUpdatedWatcher = scope.$on('searchUpdated', function(event, query) { setServerFacetSearch(scope, query); // clear each time since Smart-Table // search is cumulative tableCtrl.tableState().search = {}; // filter the smart table per column query.split('&').forEach(function(x) { $timeout(function() { var arr = x.split('='); var predString = arr[0]; var predicates = predString.split('.'); if (scope.filterFacets) { var isServerFacet = scope.filterFacets.some(function checkIsServer(facet) { return facet.name === predString && facet.isServer; }); if (isServerFacet) { return; } } // Allow nested property search if (predicates.length > 1) { var firstPred = predicates[0]; var predicateObj = {}; setPredObj(predicates, predicateObj, arr[1]); tableCtrl.search(predicateObj[firstPred], firstPred); } else { tableCtrl.search(arr[1], predString); } }); }); }); scope.$on('$destroy', function () { textSearchWatcher(); searchUpdatedWatcher(); }); } } })(); horizon-9.0.0/horizon/static/framework/widgets/magic-search/magic-search.module.js0000664000567000056710000000251312701407063031464 0ustar jenkinsjenkins00000000000000/* * 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'; /** * @ngdoc overview * @name MagicSearch * @description * * # MagicSearch * * The `MagicSearch` module provides support faceted search. This is * used with the Eucalyptus third-party MagicSearch module at * https://github.com/eucalyptus/magic-search. * * | Directives | * |-------------------------------------------------------------------| * | {@link MagicSearch.directive:hzMagicSearchBar `hzMagicSearchBar`} | * | {@link MagicSearch.directive:magicOverrides `magicOverrides`} | * | {@link MagicSearch.directive:stMagicSearch `stMagicSearch`} | * */ angular .module('horizon.framework.widgets.magic-search', ['ui.bootstrap']); })(); horizon-9.0.0/horizon/static/framework/widgets/magic-search/magic-search.controller.js0000664000567000056710000002773012701407063032372 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @fileOverview Magic Search JS * @requires AngularJS * */ angular.module('horizon.framework.widgets.magic-search') .controller('MagicSearchController', magicSearchController); magicSearchController.$inject = ['$scope', '$element', '$timeout', '$window', 'horizon.framework.widgets.magic-search.service']; function magicSearchController($scope, $element, $timeout, $window, service) { var ctrl = this; var searchInput = $element.find('.search-input'); ctrl.mainPromptString = $scope.strings.prompt; // currentSearch is the list of facets representing the current search ctrl.currentSearch = []; ctrl.isMenuOpen = false; searchInput.on('keydown', keyDownHandler); searchInput.on('keyup', keyUpHandler); searchInput.on('keypress', keyPressHandler); // enable text entry when mouse clicked anywhere in search box $element.find('.search-main-area').on('click', searchMainClickHandler); // when facet clicked, add 1st part of facet and set up options ctrl.facetClicked = facetClickHandler; // when option clicked, complete facet and send event ctrl.optionClicked = optionClickHandler; // remove facet and either update filter or search ctrl.removeFacet = removeFacet; // Controller-exposed Functions // clear entire searchbar ctrl.clearSearch = clearSearch; // ctrl.textSearch is undefined, only used when a user free-enters text // Used by the template. ctrl.isMatchLabel = function(label) { return angular.isArray(label); }; // unusedFacetChoices is the list of facet types that have not been selected ctrl.unusedFacetChoices = []; // facetChoices is the list of all facet choices ctrl.facetChoices = []; initSearch(service.getSearchTermsFromQueryString($window.location.search)); emitQuery(); function initSearch(initialSearchTerms) { // Initializes both the unused choices and the full list of facets ctrl.facetChoices = service.getFacetChoicesFromFacetsParam($scope.facets_param); // resets the facets initFacets(initialSearchTerms); } function keyDownHandler($event) { var key = service.getEventCode($event); if (key === 9) { // prevent default when we can. $event.preventDefault(); } } function tabKeyUp() { if (angular.isUndefined(ctrl.facetSelected)) { if (ctrl.filteredObj.length !== 1) { return; } ctrl.facetClicked(0, '', ctrl.filteredObj[0].name); setSearchInput(''); } else { if (angular.isUndefined(ctrl.filteredOptions) || ctrl.filteredOptions.length !== 1) { return; } ctrl.optionClicked(0, '', ctrl.filteredOptions[0].key); resetState(); } } function escapeKeyUp() { setMenuOpen(false); resetState(); var textFilter = ctrl.textSearch; if (angular.isUndefined(textFilter)) { textFilter = ''; } emitTextSearch(textFilter); } function enterKeyUp() { var searchVal = searchInput.val(); // if tag search, treat as regular facet if (ctrl.facetSelected && angular.isUndefined(ctrl.facetSelected.options)) { var curr = ctrl.facetSelected; curr.name = curr.name.split('=')[0] + '=' + searchVal; curr.label[1] = searchVal; ctrl.currentSearch.push(curr); resetState(); emitQuery(); setMenuOpen(false); } else { // if text search treat as search ctrl.currentSearch = ctrl.currentSearch.filter(notTextSearch); ctrl.currentSearch.push(service.getTextFacet(searchVal, $scope.strings.text)); $scope.$apply(); setMenuOpen(false); setSearchInput(''); emitTextSearch(searchVal); ctrl.textSearch = searchVal; } ctrl.filteredObj = ctrl.unusedFacetChoices; } function notTextSearch(item) { return item.name.indexOf('text') !== 0; } function defaultKeyUp() { var searchVal = searchInput.val(); if (searchVal === '') { ctrl.filteredObj = ctrl.unusedFacetChoices; $scope.$apply(); emitTextSearch(''); if (ctrl.facetSelected && angular.isUndefined(ctrl.facetSelected.options)) { resetState(); } } else { filterFacets(searchVal); } } function keyUpHandler($event) { // handle ctrl-char input if ($event.metaKey === true) { return; } var key = service.getEventCode($event); var handlers = { 9: tabKeyUp, 27: escapeKeyUp, 13: enterKeyUp }; if (handlers[key]) { handlers[key](); } else { defaultKeyUp(); } } function keyPressHandler($event) { // handle character input var searchVal = searchInput.val(); var key = service.getEventCode($event); // Backspace, Delete, Enter, Tab, Escape if (key !== 8 && key !== 46 && key !== 13 && key !== 9 && key !== 27) { // This builds the search term as you go. searchVal = searchVal + String.fromCharCode(key).toLowerCase(); } if (searchVal === ' ') { // space and field is empty, show menu setMenuOpen(true); setSearchInput(''); return; } if (searchVal === '') { ctrl.filteredObj = ctrl.unusedFacetChoices; $scope.$apply(); emitTextSearch(''); if (ctrl.facetSelected && angular.isUndefined(ctrl.facetSelected.options)) { resetState(); } return; } // Backspace, Delete if (key !== 8 && key !== 46) { filterFacets(searchVal); } } function filterFacets(searchVal) { // try filtering facets/options.. if no facets match, do text search var filtered = []; var isTextSearch = angular.isUndefined(ctrl.facetSelected); if (isTextSearch) { ctrl.filteredObj = ctrl.unusedFacetChoices; filtered = service.getMatchingFacets(ctrl.filteredObj, searchVal); } else { // assume option search ctrl.filteredOptions = ctrl.facetOptions; if (angular.isUndefined(ctrl.facetOptions)) { // no options, assume free form text facet return; } filtered = service.getMatchingOptions(ctrl.filteredOptions, searchVal); } if (filtered.length > 0) { setMenuOpen(true); $timeout(function() { ctrl.filteredObj = filtered; }, 0.1); } else if (isTextSearch) { emitTextSearch(searchVal); setMenuOpen(false); } } function searchMainClickHandler($event) { var target = angular.element($event.target); if (target.is('.search-main-area')) { searchInput.trigger('focus'); setMenuOpen(true); } } function facetClickHandler($index) { setMenuOpen(false); var facet = ctrl.filteredObj[$index]; var label = facet.label; if (angular.isArray(label)) { label = label.join(''); } var facetParts = facet.name && facet.name.split('='); ctrl.facetSelected = service.getFacet(facetParts[0], facetParts[1], label, ''); if (angular.isDefined(facet.options)) { ctrl.filteredOptions = ctrl.facetOptions = facet.options; setMenuOpen(true); } setSearchInput(''); setPrompt(''); $timeout(function() { searchInput.focus(); }); } function optionClickHandler($index, $event, name) { setMenuOpen(false); var curr = ctrl.facetSelected; curr.name = curr.name.split('=')[0] + '=' + name; curr.label[1] = ctrl.filteredOptions[$index].label; if (angular.isArray(curr.label[1])) { curr.label[1] = curr.label[1].join(''); } ctrl.currentSearch.push(curr); resetState(); emitQuery(); } function emitTextSearch(val) { $scope.$emit('textSearch', val, $scope.filter_keys); } function emitQuery(removed) { var query = service.getQueryPattern(ctrl.currentSearch); if (angular.isDefined(removed) && removed.indexOf('text') === 0) { emitTextSearch(''); delete ctrl.textSearch; } else { $scope.$emit('searchUpdated', query); if (ctrl.currentSearch.length > 0) { // prune facets as needed from menus var newFacet = ctrl.currentSearch[ctrl.currentSearch.length - 1].name; var facetParts = service.getSearchTermObject(newFacet); service.removeChoice(facetParts, ctrl.facetChoices, ctrl.unusedFacetChoices); } } } function clearSearch() { if (ctrl.currentSearch.length > 0) { ctrl.currentSearch = []; ctrl.unusedFacetChoices = ctrl.facetChoices.map(service.getFacetChoice); resetState(); $scope.$emit('searchUpdated', ''); emitTextSearch(''); } } function resetState() { setSearchInput(''); ctrl.filteredObj = ctrl.unusedFacetChoices; delete ctrl.facetSelected; delete ctrl.facetOptions; delete ctrl.filteredOptions; if (ctrl.currentSearch.length === 0) { setPrompt(ctrl.mainPromptString); } } function setMenuOpen(bool) { $timeout(function setMenuOpenTimeout() { ctrl.isMenuOpen = bool; }); } function setSearchInput(val) { $timeout(function setSearchInputTimeout() { searchInput.val(val); }); } function setPrompt(str) { $timeout(function setPromptTimeout() { $scope.strings.prompt = str; }); } /** * Add ability to update facet * Broadcast event when facet options are returned via AJAX. * Should magic_search.js absorb this? */ var facetsChangedWatcher = $scope.$on('facetsChanged', function (event, data) { $timeout(function () { if (data && data.magicSearchQuery) { initSearch(data.magicSearchQuery.split('&')); } else { initSearch(ctrl.currentSearch.map(function(x) { return x.name; })); } }); }); $scope.$on('$destroy', function () { facetsChangedWatcher(); }); function initFacets(searchTerms) { var tmpFacetChoices = ctrl.facetChoices.map(service.getFacetChoice); if (searchTerms.length > 1 || searchTerms[0] && searchTerms[0].length > 0) { setPrompt(''); } ctrl.currentSearch = service.getFacetsFromSearchTerms(searchTerms, ctrl.textSearch, $scope.strings.text, tmpFacetChoices); ctrl.filteredObj = ctrl.unusedFacetChoices = service.getUnusedFacetChoices(tmpFacetChoices, searchTerms); // broadcast to check facets for server-side $scope.$emit('checkFacets', ctrl.currentSearch); } /** * Override magic_search.js 'removeFacet' to emit('checkFacets') * to flag facets as 'isServer' after removing facet and * either update filter or search * @param {number} index - the index of the facet to remove. Required. * * @returns {number} Doesn't return anything */ function removeFacet(index) { var removed = ctrl.currentSearch[index].name; ctrl.currentSearch.splice(index, 1); if (angular.isUndefined(ctrl.facetSelected)) { emitQuery(removed); } else { resetState(); } if (ctrl.currentSearch.length === 0) { setPrompt(ctrl.mainPromptString); } // re-init to restore facets cleanly initFacets(ctrl.currentSearch.map(service.getName)); } } })(); horizon-9.0.0/horizon/static/framework/widgets/magic-search/magic-search.controller.spec.js0000664000567000056710000006645112701407063033326 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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"; describe('MagicSearchController', function () { var ctrl, scope, searchInput, $timeout, service; function expectResetState() { expect(ctrl.facetSelected).toBeUndefined(); expect(ctrl.facetOptions).toBeUndefined(); expect(ctrl.filteredOptions).toBeUndefined(); expect(ctrl.filteredObj).toBe(ctrl.unusedFacetChoices); } // Given an array of handlername:function items, return the // function for the given name. function getHandler(args, name) { return args.reduce(function (last, curr) { last[curr[0]] = curr[1]; return last; }, {})[name]; } beforeEach(module('horizon.framework.widgets.magic-search')); beforeEach(inject(function($controller, _$timeout_, $window, $rootScope, $injector) { $timeout = _$timeout_; scope = $rootScope.$new(); scope.strings = { prompt: "Hello World!" }; scope.facets_param = []; service = $injector.get('horizon.framework.widgets.magic-search.service'); searchInput = { on: angular.noop, val: function() { return ''; }, focus: angular.noop }; spyOn(searchInput, 'on'); var $element = { find: function() { return searchInput; }}; ctrl = $controller('MagicSearchController', { $scope: scope, $element: $element, $timeout: $timeout, $window: $window }); })); it("defines the controller", function() { expect(ctrl).toBeDefined(); }); describe("filterFacets", function() { var execFilter; beforeEach(function() { var keyUpHandler = getHandler(searchInput.on.calls.allArgs(), 'keyup'); var evt = { keyCode: -1, charCode: 10, preventDefault: angular.noop }; execFilter = function() { keyUpHandler(evt); }; spyOn(searchInput, 'val').and.returnValue('hello'); ctrl.facetSelected = {}; ctrl.filteredObj = []; }); it("sets the filteredObj if results to a text search", function() { delete ctrl.facetSelected; spyOn(service, 'getMatchingFacets').and.returnValue(['my', 'list']); execFilter(); $timeout.flush(); expect(ctrl.filteredObj).toEqual(['my', 'list']); }); it("closes the menu if no results to a text search", function() { delete ctrl.facetSelected; ctrl.isMenuOpen = true; spyOn(service, 'getMatchingFacets').and.returnValue([]); execFilter(); $timeout.flush(); expect(ctrl.isMenuOpen).toBe(false); }); it("sets filteredObj with results of an option search", function() { ctrl.facetOptions = []; ctrl.isMenuOpen = true; spyOn(service, 'getMatchingOptions').and.returnValue(['my', 'list']); execFilter(); $timeout.flush(); expect(ctrl.filteredObj).toEqual(['my', 'list']); }); it("doesn't set filteredObj with no results of an option search", function() { ctrl.facetOptions = []; ctrl.isMenuOpen = true; spyOn(service, 'getMatchingOptions').and.returnValue([]); execFilter(); expect(ctrl.filteredObj).toEqual([]); }); it("doesn't set filteredObj with no facet options", function() { execFilter(); expect(ctrl.filteredObj).toEqual([]); }); }); describe("clearSearch", function() { it("does nothing when currentSearch is empty", function() { spyOn(scope, '$emit'); ctrl.currentSearch = []; ctrl.clearSearch(); expect(scope.$emit).not.toHaveBeenCalled(); }); it("clears the currentSearch when currentSearch is not empty", function() { spyOn(scope, '$emit'); ctrl.currentSearch = ['a', 'b', 'c']; scope.filter_keys = [1,2,3]; ctrl.clearSearch(); expect(scope.$emit).toHaveBeenCalledWith('searchUpdated', ''); expect(scope.$emit).toHaveBeenCalledWith('textSearch', '', [1,2,3]); }); }); describe("keydown handler", function() { var keyDownHandler; var evt = {keyCode: 10, charCode: 10, preventDefault: angular.noop}; beforeEach(function() { keyDownHandler = getHandler(searchInput.on.calls.allArgs(), 'keydown'); }); it("is defined", function() { expect(keyDownHandler).toBeDefined(); }); it("does nothing with keys other than 9", function() { spyOn(evt, 'preventDefault'); keyDownHandler(evt); expect(evt.preventDefault).not.toHaveBeenCalled(); }); it("does call preventDefault with keycode of 9", function() { evt.keyCode = 9; spyOn(evt, 'preventDefault'); keyDownHandler(evt); expect(evt.preventDefault).toHaveBeenCalled(); }); }); describe("keyup handler", function() { var keyUpHandler, evt; beforeEach(function() { keyUpHandler = getHandler(searchInput.on.calls.allArgs(), 'keyup'); evt = {keyCode: 10, charCode: 10, preventDefault: angular.noop}; }); it("is defined", function() { expect(keyUpHandler).toBeDefined(); }); it("doesn't emit anything if sent a metakey", function() { evt.metaKey = true; spyOn(scope, '$emit'); keyUpHandler(evt); expect(scope.$emit).not.toHaveBeenCalled(); }); describe("'Escape' key", function() { beforeEach(function() { evt.keyCode = 27; }); it("closes the menu", function() { ctrl.isMenuOpen = true; keyUpHandler(evt); $timeout.flush(); expect(ctrl.isMenuOpen).toBe(false); }); it("closes the menu (using charCode)", function() { ctrl.isMenuOpen = true; delete evt.keyCode; evt.charCode = 27; keyUpHandler(evt); $timeout.flush(); expect(ctrl.isMenuOpen).toBe(false); }); it("emits a textSearch event", function() { ctrl.textSearch = 'waldo'; scope.filter_keys = 'abc'; spyOn(scope, '$emit'); keyUpHandler(evt); expect(scope.$emit).toHaveBeenCalledWith('textSearch', 'waldo', 'abc'); }); it("emits a textSearch event even if ctrl.textSearch undefined", function() { delete ctrl.textSearch; scope.filter_keys = 'abc'; spyOn(scope, '$emit'); keyUpHandler(evt); expect(scope.$emit).toHaveBeenCalledWith('textSearch', '', 'abc'); }); }); describe("'Tab' key", function() { beforeEach(function() { evt.keyCode = 9; ctrl.facetSelected = {}; }); it("calls facetClicked when no facet selected and exactly one facet", function() { spyOn(ctrl, 'facetClicked'); delete ctrl.facetSelected; ctrl.filteredObj = [{name: 'waldo'}]; keyUpHandler(evt); expect(ctrl.facetClicked).toHaveBeenCalledWith(0, '', 'waldo'); }); it("doesn't call facetClicked when no facet selected and not one facet", function() { spyOn(ctrl, 'facetClicked'); delete ctrl.facetSelected; ctrl.filteredObj = [{name: 'waldo'}, {name: 'warren'}]; keyUpHandler(evt); expect(ctrl.facetClicked).not.toHaveBeenCalled(); }); it("calls optionClicked when a facet selected and one option", function() { spyOn(ctrl, 'optionClicked'); ctrl.filteredOptions = [{key: 'thekey'}]; keyUpHandler(evt); expect(ctrl.optionClicked).toHaveBeenCalledWith(0, '', 'thekey'); expectResetState(); }); it("doesn't call optionClicked when a facet selected and not one option", function() { spyOn(ctrl, 'optionClicked'); ctrl.filteredOptions = [{key: 'thekey'}, {key: 'another'}]; keyUpHandler(evt); expect(ctrl.optionClicked).not.toHaveBeenCalled(); }); it("sets searchInput to an empty string", function() { spyOn(searchInput, 'val'); delete ctrl.facetSelected; ctrl.filteredObj = [{name: 'waldo'}]; keyUpHandler(evt); $timeout.flush(); expect(searchInput.val).toHaveBeenCalledWith(''); }); }); describe("'Enter' key", function() { beforeEach(function() { evt.keyCode = 13; ctrl.facetSelected = {name: 'waldo=undefined', label: ['a']}; }); it("hides menu if facet is selected", function() { ctrl.isMenuOpen = true; keyUpHandler(evt); $timeout.flush(); expect(ctrl.isMenuOpen).toBe(false); }); it("sets menu closed if facet is not selected", function() { ctrl.isMenuOpen = true; delete ctrl.facetSelected; keyUpHandler(evt); $timeout.flush(); expect(ctrl.isMenuOpen).toBe(false); }); it("removes currentSearch item names starting with 'text'", function() { delete ctrl.facetSelected; spyOn(searchInput, 'val').and.returnValue('searchval'); scope.strings.text = 'stringtext'; ctrl.currentSearch = [{name: 'textstuff'}, {name: 'texting'}, {name: 'nontext'}, {name: 'nottext'}]; keyUpHandler(evt); expect(ctrl.currentSearch).toEqual([{name: 'nontext'}, {name: 'nottext'}, {name: 'text=searchval', label: ['stringtext', 'searchval']}]); }); }); describe("Any other key", function() { beforeEach(function() { evt.keyCode = -1; }); it("performs a text search if the search is an empty string", function() { spyOn(searchInput, 'val').and.returnValue(''); spyOn(scope, '$emit'); scope.filter_keys = ['a', 'b', 'c']; keyUpHandler(evt); expect(scope.$emit).toHaveBeenCalledWith('textSearch', '', ['a', 'b', 'c']); }); it("resets state if facetSelected and no options", function() { spyOn(searchInput, 'val').and.returnValue(''); scope.filter_keys = ['a', 'b', 'c']; ctrl.facetSelected = {}; keyUpHandler(evt); expectResetState(); }); it("filters if there is a search term", function() { spyOn(searchInput, 'val').and.returnValue('searchterm'); spyOn(scope, '$emit'); scope.filter_keys = [1,2,3]; keyUpHandler(evt); expect(scope.$emit).toHaveBeenCalledWith('textSearch', 'searchterm', [1,2,3]); }); }); }); describe("keypress handler", function() { var keyPressHandler, evt; beforeEach(function() { keyPressHandler = getHandler(searchInput.on.calls.allArgs(), 'keypress'); evt = {which: 65, keyCode: 10, charCode: 10, preventDefault: angular.noop}; }); it("is defined", function() { expect(keyPressHandler).toBeDefined(); }); it("searches for searchterm and 'e' if 'e' is typed", function() { evt.which = 69; spyOn(searchInput, 'val').and.returnValue('man'); spyOn(scope, '$emit'); scope.filter_keys = [1,2,3]; keyPressHandler(evt); expect(scope.$emit).toHaveBeenCalledWith('textSearch', 'mane', [1,2,3]); }); it("opens menu when searchVal is a space", function() { evt.which = 32; spyOn(searchInput, 'val').and.returnValue(' '); spyOn(scope, '$emit'); scope.filter_keys = [1,2,3]; keyPressHandler(evt); expect(scope.$emit).toHaveBeenCalledWith('textSearch', ' ', [1,2,3]); }); it("opens menu when searchVal is an empty string", function() { spyOn(searchInput, 'val').and.returnValue(''); spyOn(scope, '$emit'); evt.which = 13; // not alter search scope.filter_keys = [1,2,3]; keyPressHandler(evt); expect(scope.$emit).toHaveBeenCalledWith('textSearch', '', [1,2,3]); }); it("resets state when ctrl.facetSelected exists but has no options", function() { spyOn(searchInput, 'val').and.returnValue(''); spyOn(scope, '$emit'); evt.which = 13; // not alter search scope.filter_keys = [1,2,3]; ctrl.facetSelected = {}; ctrl.facetOptions = {}; ctrl.filteredOptions = {}; keyPressHandler(evt); expect(scope.$emit).toHaveBeenCalledWith('textSearch', '', [1,2,3]); expectResetState(); }); it("filters when searchval has content and key is not delete/backspace", function() { spyOn(searchInput, 'val').and.returnValue('searchterm'); spyOn(scope, '$emit'); evt.which = 13; // not alter search scope.filter_keys = [1,2,3]; keyPressHandler(evt); expect(scope.$emit).toHaveBeenCalledWith('textSearch', 'searchterm', [1,2,3]); }); it("does not filter when key is backspace/delete", function() { spyOn(searchInput, 'val').and.returnValue('searchterm'); spyOn(scope, '$emit'); evt.which = 8; // not alter search keyPressHandler(evt); expect(scope.$emit).not.toHaveBeenCalled(); }); }); describe("optionClicked", function() { it("closes the menu", function() { ctrl.isMenuOpen = true; ctrl.facetSelected = {name: 'waldo', label: []}; ctrl.filteredOptions = [{label: 'meow'}]; ctrl.optionClicked(0); $timeout.flush(); expect(ctrl.isMenuOpen).toBe(false); }); it("resets state", function() { ctrl.facetSelected = {name: 'waldo', label: []}; ctrl.filteredOptions = [{label: 'meow'}]; ctrl.optionClicked(0); $timeout.flush(); expectResetState(); }); it("adds to the current search", function() { ctrl.facetSelected = {name: 'waldo', label: ['a']}; ctrl.filteredOptions = [{label: 'meow'}]; ctrl.currentSearch = []; var nothing; ctrl.optionClicked(0, nothing, 'missing'); expect(ctrl.currentSearch).toEqual([{name: 'waldo=missing', label: ['a', 'meow']}]); }); it("adds to the current search, concatting labels", function() { ctrl.facetSelected = {name: 'waldo=undefined', label: ['a']}; ctrl.filteredOptions = [{label: ['me', 'ow']}]; ctrl.currentSearch = []; var nothing; ctrl.optionClicked(0, nothing, 'missing'); expect(ctrl.currentSearch).toEqual([{name: 'waldo=missing', label: ['a', 'meow']}]); }); }); describe("removeFacet", function() { it("clears the currentSearch", function() { ctrl.currentSearch = [{}]; ctrl.removeFacet(0); expect(ctrl.currentSearch).toEqual([]); }); it("resets the main prompt if no more facets", function() { ctrl.currentSearch = [{}]; scope.strings.prompt = 'aha'; ctrl.mainPromptString = 'bon jovi'; ctrl.removeFacet(0); $timeout.flush(); expect(scope.strings.prompt).toEqual('bon jovi'); }); it("resets main prompt to blank if facets remain", function() { ctrl.currentSearch = [{}, {name: 'waldo'}]; scope.strings.prompt = 'aha'; ctrl.mainPromptString = 'bon jovi'; ctrl.removeFacet(0); $timeout.flush(); expect(scope.strings.prompt).toEqual(''); }); it("resets state if facet selected", function() { ctrl.currentSearch = [{}]; ctrl.facetSelected = {}; ctrl.removeFacet(0); expectResetState(); }); }); describe("facetClicked", function() { it("closes the menu", function() { ctrl.isMenuOpen = true; ctrl.filteredObj = [{name: 'a=b', label: 'ok'}]; ctrl.facetClicked(0); $timeout.flush(); expect(ctrl.isMenuOpen).toBe(false); }); it("sets the prompt to an empty string", function() { scope.strings.prompt = 'aha'; ctrl.filteredObj = [{name: 'a=b', label: 'ok'}]; ctrl.facetClicked(0); $timeout.flush(); expect(scope.strings.prompt).toBe(''); }); it("sets focus on the search input", function() { ctrl.filteredObj = [{name: 'a=b', label: 'ok'}]; spyOn(searchInput, 'focus'); ctrl.facetClicked(0); $timeout.flush(); expect(searchInput.focus).toHaveBeenCalled(); }); it("sets facetSelected properly", function() { ctrl.filteredObj = [{name: 'name=waldo', label: 'ok'}]; ctrl.facetClicked(0); $timeout.flush(); expect(ctrl.facetSelected).toEqual({name: 'name=waldo', label: ['ok', '']}); }); it("sets facetSelected properly if the label is an array", function() { ctrl.filteredObj = [{name: 'name=waldo', label: ['o', 'k']}]; ctrl.facetClicked(0); $timeout.flush(); expect(ctrl.facetSelected).toEqual({name: 'name=waldo', label: ['ok', '']}); }); it("sets options if present in the filteredObj", function() { ctrl.filteredObj = [{name: 'name=waldo', label: 'ok', options: [1,2,3]}]; ctrl.facetClicked(0); $timeout.flush(); expect(ctrl.filteredOptions).toEqual([1,2,3]); expect(ctrl.facetOptions).toEqual([1,2,3]); }); it("opens the menu if options present in the filteredObj", function() { ctrl.isMenuOpen = false; ctrl.filteredObj = [{name: 'name=waldo', label: 'ok', options: [1,2,3]}]; ctrl.facetClicked(0); $timeout.flush(); expect(ctrl.isMenuOpen).toBe(true); }); }); }); // NOTE: The javascript file being tested here isn't the magic-search code // as a whole, but instead the magic-overrides code. describe('MagicSearch module', function () { it('should be defined', function () { expect(angular.module('horizon.framework.widgets.magic-search')).toBeDefined(); }); }); /* xdescribe('magic-overrides directive', function () { var $window, $scope, $magicScope, $timeout; beforeEach(module('templates')); beforeEach(module('horizon.framework.widgets.magic-search')); beforeEach(module(function ($provide) { $provide.value('$window', { location: { search: '' } }); })); beforeEach(inject(function ($injector) { $window = $injector.get('$window'); var $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); $timeout = $injector.get('$timeout'); $scope.filterStrings = { cancel: gettext('Cancel'), prompt: gettext('Prompt'), remove: gettext('Remove'), text: gettext('Text') }; $scope.filterFacets = [ { name: 'name', label: gettext('Name'), singleton: true }, { name: 'status', label: gettext('Status'), options: [ { key: 'active', label: gettext('Active') }, { key: 'shutdown', label: gettext('Shutdown') }, { key: 'error', label: gettext('Error') } ] }, { name: 'flavor', label: gettext('Flavor'), singleton: true, options: [ { key: 'm1.tiny', label: gettext('m1.tiny') }, { key: 'm1.small', label: gettext('m1.small') } ] } ]; // eslint-disable angular/ng_window_service // var markup = '' + ''; // eslint-enable angular/ng_window_service // $scope.$apply(); $magicScope = $scope.$$childTail; //eslint-disable-line angular/ng_no_private_call spyOn($magicScope, '$emit'); spyOn($magicScope, 'emitQuery'); spyOn($magicScope, 'deleteFacetEntirely').and.callThrough(); spyOn($magicScope, 'deleteFacetSelection').and.callThrough(); spyOn($magicScope, 'initSearch'); spyOn($magicScope, 'resetState'); })); it('isMenuOpen should be initially false', function () { expect($magicScope.isMenuOpen).toBe(false); }); it('isMenuOpen should be true after showMenu called', function () { $magicScope.showMenu(); $timeout.flush(); expect($magicScope.isMenuOpen).toBe(true); }); it('isMenuOpen should be false after hideMenu called', function () { $magicScope.showMenu(); $timeout.flush(); $magicScope.hideMenu(); $timeout.flush(); expect($magicScope.isMenuOpen).toBe(false); }); it('initSearch should be called when facetsChanged broadcasted', function () { $scope.$broadcast('facetsChanged'); $timeout.flush(); expect($magicScope.currentSearch).toEqual([]); expect($magicScope.initSearch).toHaveBeenCalled(); }); it('currentSearch should be empty when URL has no search terms', function () { expect($magicScope.currentSearch).toEqual([]); }); describe('initFacets', function () { it('currentSearch should have one item when URL has one search term', function () { $window.location.search = '?name=myname'; $magicScope.initFacets(); $timeout.flush(); expect($magicScope.currentSearch.length).toBe(1); expect($magicScope.currentSearch[0].label).toEqual([ 'Name', 'myname' ]); expect($magicScope.currentSearch[0].name).toBe('name=myname'); expect($magicScope.strings.prompt).toBe(''); // 'name' facet should be deleted (singleton) expect($magicScope.deleteFacetEntirely).toHaveBeenCalledWith([ 'name', 'myname' ]); }); it('currentSearch should have one item when given one search term', function () { var currentFacets = [{name: 'name=myname'}]; $magicScope.initFacets(currentFacets); $timeout.flush(); expect($magicScope.currentSearch.length).toBe(1); expect($magicScope.currentSearch[0].label).toEqual([ 'Name', 'myname' ]); expect($magicScope.currentSearch[0].name).toBe('name=myname'); // 'name' facet should be deleted (singleton) expect($magicScope.deleteFacetEntirely).toHaveBeenCalledWith([ 'name', 'myname' ]); }); it('currentSearch should have two items when given two search terms', function () { var currentFacets = [{name: 'name=myname'}, {name: 'status=active'}]; $magicScope.initFacets(currentFacets); $timeout.flush(); // only 'active' option should be removed from 'status' facet (not singleton) expect($magicScope.currentSearch.length).toBe(2); expect($magicScope.deleteFacetSelection).toHaveBeenCalledWith([ 'status', 'active' ]); }); it('flavor facet should be removed if search term includes flavor', function () { var currentFacets = [{name: 'flavor=m1.tiny'}]; $magicScope.initFacets(currentFacets); $timeout.flush(); // entire 'flavor' facet should be removed even if some options left (singleton) expect($magicScope.deleteFacetEntirely).toHaveBeenCalledWith([ 'flavor', 'm1.tiny' ]); }); it('currentSearch should have one item when search is textSearch', function () { $magicScope.textSearch = 'test'; $magicScope.initFacets([]); $timeout.flush(); expect($magicScope.currentSearch[0].label).toEqual([ 'Text', 'test' ]); expect($magicScope.currentSearch[0].name).toBe('text=test'); }); it('currentSearch should have textSearch and currentSearch', function () { $magicScope.textSearch = 'test'; $magicScope.initFacets([{name: 'flavor=m1.tiny'}]); $timeout.flush(); expect($magicScope.currentSearch.length).toBe(2); expect($magicScope.currentSearch[0].label).toEqual([ 'Flavor', 'm1.tiny' ]); expect($magicScope.currentSearch[0].name).toBe('flavor=m1.tiny'); expect($magicScope.currentSearch[1].label).toEqual([ 'Text', 'test' ]); expect($magicScope.currentSearch[1].name).toBe('text=test'); }); it('should call checkFacets when initFacets called', function () { $magicScope.initFacets([]); expect($magicScope.$emit).toHaveBeenCalledWith('checkFacets', []); }); }); describe('removeFacet', function () { beforeEach(function () { spyOn($magicScope, 'initFacets').and.callThrough(); }); it('should call emitQuery, initFacets and emit checkFacets on removeFacet', function () { var initialSearch = { name: 'name=myname', label: [ 'Name', 'myname' ] }; $magicScope.currentSearch.push(initialSearch); $magicScope.removeFacet(0); expect($magicScope.currentSearch).toEqual([]); expect($magicScope.emitQuery).toHaveBeenCalledWith('name=myname'); expect($magicScope.initFacets).toHaveBeenCalledWith([]); expect($magicScope.$emit).toHaveBeenCalledWith('checkFacets', []); expect($magicScope.strings.prompt).toBe('Prompt'); }); it('prompt text === "" if search terms left after removal of one', function () { $magicScope.strings.prompt = ''; $magicScope.currentSearch.push({ name: 'name=myname', label: [ 'Name', 'myname' ] }); $magicScope.currentSearch.push({ name: 'status=active', label: [ 'Status', 'Active' ] }); $magicScope.removeFacet(0); expect($magicScope.strings.prompt).toBe(''); }); it('should emit checkFacets on removeFacet if facetSelected', function () { var initialSearch = { name: 'name=myname', label: [ 'Name', 'myname' ] }; $magicScope.currentSearch.push(initialSearch); $magicScope.facetSelected = { 'name': 'status', 'label': [ 'Status', 'active' ] }; $magicScope.removeFacet(0); expect($magicScope.currentSearch).toEqual([]); expect($magicScope.resetState).toHaveBeenCalled(); expect($magicScope.initFacets).toHaveBeenCalledWith([]); expect($magicScope.$emit).toHaveBeenCalledWith('checkFacets', []); }); it('should emit checkFacets and remember state on removeFacet if facetSelected', function () { var search1 = { name: 'name=myname', label: [ 'Name', 'myname' ] }; var search2 = { name: 'flavor=m1.tiny', label: [ 'Flavor', 'm1.tiny' ] }; $magicScope.currentSearch.push(search1); $magicScope.currentSearch.push(search2); $magicScope.facetSelected = { 'name': 'status', 'label': [ 'Status', 'active' ] }; $magicScope.removeFacet(0); expect($magicScope.currentSearch).toEqual([search2]); expect($magicScope.resetState).toHaveBeenCalled(); expect($magicScope.initFacets).toHaveBeenCalledWith([search2]); expect($magicScope.$emit).toHaveBeenCalledWith('checkFacets', [search2]); }); }); }); */ })(); horizon-9.0.0/horizon/static/framework/widgets/magic-search/magic-search.html0000664000567000056710000000530712701407063030534 0ustar jenkinsjenkins00000000000000 horizon-9.0.0/horizon/static/framework/widgets/magic-search/st-magic-search.directive.spec.js0000664000567000056710000001752212701407063033540 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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"; describe('st-magic-search directive', function () { var $element, $scope, $timeout; beforeEach(module('templates')); beforeEach(module('smart-table')); beforeEach(module('horizon.framework.widgets')); beforeEach(module('horizon.framework.widgets.magic-search')); beforeEach(module(function ($provide) { $provide.value('$window', { location: { search: '', href: '' }, history: { pushState: angular.noop } }); })); beforeEach(inject(function ($injector) { var $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); $timeout = $injector.get('$timeout'); $scope.rows = [ { name: 'name 1', server_name: 'server 1', status: 'active', flavor: 'm1.tiny' }, { name: 'name 2', server_name: 'server 2', status: 'active', flavor: 'm1.small' }, { name: 'name 3', server_name: 'server 3', status: 'shutdown', flavor: 'm1.tiny' }, { name: 'name 4', server_name: 'server 4', status: 'shutdown', flavor: 'm1.small' }, { name: 'name 5', server_name: 'server 5', status: 'error', flavor: 'm1.tiny' }, { name: 'name 6', server_name: 'server 6', status: 'error', flavor: 'm1.small' } ]; $scope.filterStrings = { cancel: gettext('Cancel'), prompt: gettext('Prompt'), remove: gettext('Remove'), text: gettext('Text') }; $scope.filterFacets = [ { name: 'name', label: gettext('Name'), singleton: true }, { name: 'server_name', label: gettext('Server Name'), singleton: true, isServer: true }, { name: 'status', label: gettext('Status'), options: [ { key: 'active', label: gettext('Active') }, { key: 'shutdown', label: gettext('Shutdown') }, { key: 'error', label: gettext('Error') } ] }, { name: 'flavor', label: gettext('Flavor'), singleton: true, options: [ { key: 'm1.tiny', label: gettext('m1.tiny') }, { key: 'm1.small', label: gettext('m1.small') } ] } ]; /* eslint-disable angular/window-service */ var msTemplate = window.STATIC_URL + 'framework/widgets/magic-search/magic-search.html'; /* eslint-enable angular/window-service */ var stMagicSearch = '' + ' ' + ' ' + ''; var markup = '' + '' + ' ' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' ' + ' ' + '' + '
' + stMagicSearch + '
{{ row.name }}{{ row.status }}
'; $element = $compile(angular.element(markup)); $scope.$apply(); })); it('should filter table to two rows if text searching with "shutdown"', function () { var element = $element($scope); $scope.$apply(); $scope.$broadcast('textSearch', 'shutdown'); $timeout.flush(); expect(element.find('tbody tr').length).toBe(2); }); it('should skip text searching if clientFullTextSearch is false and raise events', function () { spyOn($scope, '$emit').and.callThrough(); $scope.clientFullTextSearch = false; var element = $element($scope); $scope.$apply(); $scope.$broadcast('textSearch', 'active'); $timeout.flush(); expect(element.find('tbody tr').length).toBe(6); expect($scope.$emit).toHaveBeenCalledWith( 'serverSearchUpdated', { //magicSearchQuery: '', magicSearchQueryChanged: false, queryString: 'active', queryStringChanged: true } ); }); it('should not raise serverSearchUpdated event if nothing has changed', function () { spyOn($scope, '$emit').and.callThrough(); $scope.clientFullTextSearch = false; var element = $element($scope); $scope.$apply(); $scope.$broadcast('textSearch', 'active'); $timeout.flush(); $scope.$broadcast('textSearch', 'active'); $timeout.flush(); expect(element.find('tbody tr').length).toBe(6); /* expect($scope.$emit).toHaveBeenCalledWith( 'serverSearchUpdated', { magicSearchQuery: '', magicSearchQueryChanged: true, queryStringChanged: false } ); */ expect($scope.$emit).toHaveBeenCalledWith( 'serverSearchUpdated', { //magicSearchQuery: '', magicSearchQueryChanged: false, queryString: 'active', queryStringChanged: true } ); // Originally expected to be 2. expect($scope.$emit.calls.count()).toEqual(1); }); it('should filter table to two rows if facet with static === "shutdown"', function () { var element = $element($scope); $scope.$apply(); $scope.$broadcast('searchUpdated', 'status=shutdown'); $timeout.flush(); expect(element.find('tbody tr').length).toBe(2); }); it('should filter table to 1 row if facet with name === "name 1"', function () { var element = $element($scope); $scope.$apply(); $scope.$broadcast('searchUpdated', 'name=name 1'); $scope.$broadcast('textSearch', 'active'); $timeout.flush(); expect(element.find('tbody tr').length).toBe(1); }); it('should not filter table if filter is server side and raise event', function () { spyOn($scope, '$emit').and.callThrough(); var element = $element($scope); $scope.$apply(); $scope.$broadcast('searchUpdated', 'server_name=server 1'); $timeout.flush(); expect(element.find('tbody tr').length).toBe(6); expect($scope.$emit).toHaveBeenCalledWith( 'serverSearchUpdated', { magicSearchQuery: 'server_name=server 1', magicSearchQueryChanged: true, queryStringChanged: false } ); }); it('should not raise serverSearchUpdated if filter has not changed', function () { spyOn($scope, '$emit').and.callThrough(); var element = $element($scope); $scope.$apply(); $scope.$broadcast('searchUpdated', 'server_name=server 1'); $timeout.flush(); $scope.$broadcast('searchUpdated', 'server_name=server 1'); $timeout.flush(); expect(element.find('tbody tr').length).toBe(6); expect($scope.$emit).toHaveBeenCalledWith( 'serverSearchUpdated', { magicSearchQuery: 'server_name=server 1', magicSearchQueryChanged: true, queryStringChanged: false } ); // Original expectation was 2. expect($scope.$emit.calls.count()).toEqual(1); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/magic-search/magic-search.directive.js0000664000567000056710000000230012701407063032147 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @fileOverview Magic Search JS * @requires AngularJS * */ angular.module('horizon.framework.widgets.magic-search') .directive('magicSearch', magicSearch); magicSearch.$inject = []; function magicSearch() { return { restrict: 'E', scope: { facets_param: '@facets', filter_keys: '=filterKeys', strings: '=strings' }, templateUrl: function (scope, elem) { return elem.template; }, controller: 'MagicSearchController', controllerAs: 'ctrl' }; } })(); horizon-9.0.0/horizon/static/framework/widgets/modal/0000775000567000056710000000000012701407231024064 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/modal/wizard-modal.service.js0000664000567000056710000000443512701407063030464 0ustar jenkinsjenkins00000000000000/** * 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'; /** * @ngdoc service * @name horizon.framework.widgets.modal.wizard-modal.service * * @description * Horizon's wrapper for angular-bootstrap modal service. * It should only be use for Wizard dialogs. * * @example: * angular * .controller('modalExampleCtrl', ExampleCtrl); * * ExampleCtrl.$inject = [ * '$scope', * 'horizon.framework.widgets.modal.wizard-modal.service' * ]; * * function ExampleCtrl($scope, wizardModalService) { * var options = { * scope: scope, // the scope to use for the Wizard * workflow: workflow, // the workflow used in the wizard * submit: submit // callback to call on a wizard submit * }; * * wizardModalService(options); * }); */ angular .module('horizon.framework.widgets.modal') .factory('horizon.framework.widgets.modal.wizard-modal.service', WizardModalService); WizardModalService.$inject = [ '$modal' ]; function WizardModalService($modal) { var service = { modal: modal }; return service; //////////////////// function modal(params) { if (params && params.scope && params.workflow && params.submit) { var options = { size: 'lg', controller: 'WizardModalController as modalCtrl', scope: params.scope, template: '', backdrop: 'static', windowClass: 'modal-dialog-wizard', resolve: { workflow: function() { return params.workflow; }, submit: function() { return params.submit; } } }; return $modal.open(options); } } } })(); horizon-9.0.0/horizon/static/framework/widgets/modal/delete-modal.service.spec.js0000664000567000056710000001206612701407063031356 0ustar jenkinsjenkins00000000000000/** * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use self 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'; describe('horizon.framework.widgets.modal.deleteModalService', function() { var labels = { title: gettext('Confirm Delete Foobars'), message: gettext('selected "%s"'), submit: gettext('Delete'), success: gettext('Deleted : %s.'), error: gettext('Unable to delete: %s.') }; var entityAPI = { deleteEntity: function(entityId) { var deferred = $q.defer(); if (entityId === 'bad') { deferred.reject(); } else { deferred.resolve(); } return deferred.promise; } }; var simpleModalService = { modal: function () { return { result: { then: function (callback) { callback(); } } }; } }; var toastService = { add: function() {} }; var $scope, $q, service; function getContext() { return { labels: labels, deleteEntity: entityAPI.deleteEntity, successEvent: 'custom_delete_event_passed', failedEvent: 'custom_delete_event_failed' }; } /////////////////////// beforeEach(module('horizon.framework.util')); beforeEach(module('horizon.framework.widgets')); beforeEach(module(function($provide) { $provide.value('horizon.framework.widgets.modal.simple-modal.service', simpleModalService); $provide.value('horizon.framework.widgets.toast.service', toastService); })); beforeEach(inject(function($injector, _$rootScope_) { $scope = _$rootScope_.$new(); $q = $injector.get('$q'); service = $injector.get('horizon.framework.widgets.modal.deleteModalService'); })); it('should open the modal with correct message', function() { var fakeModalService = { result: { then: function () {} } }; var entities = [ {name: 'entity1', id: '1'}, {id: '2'} ]; spyOn(simpleModalService, 'modal').and.returnValue(fakeModalService); service.open($scope, entities, getContext()); expect(simpleModalService.modal).toHaveBeenCalled(); var args = simpleModalService.modal.calls.argsFor(0)[0]; expect(args.body).toEqual('selected "entity1", "2"'); }); it('should call entityAPI to delete entities and raise events', function() { spyOn(toastService, 'add').and.callThrough(); spyOn($scope, '$emit').and.callThrough(); spyOn(entityAPI, 'deleteEntity').and.callThrough(); var entities = [ {name: 'entity1', id: '1'}, {name: 'entity2', id: '2'} ]; service.open($scope, entities, getContext()); $scope.$apply(); expect(entityAPI.deleteEntity).toHaveBeenCalledWith('1'); expect(entityAPI.deleteEntity).toHaveBeenCalledWith('2'); expect(toastService.add).toHaveBeenCalledWith('success', 'Deleted : entity1, entity2.'); expect($scope.$emit).toHaveBeenCalledWith('custom_delete_event_passed', [ '1', '2' ]); }); it('should raise failed events if Entity is not deleted', function() { spyOn(toastService, 'add').and.callThrough(); spyOn($scope, '$emit').and.callThrough(); spyOn(entityAPI, 'deleteEntity').and.callThrough(); var entities = [{name: 'entity1', id: 'bad'}]; service.open($scope, entities, getContext()); $scope.$apply(); expect(entityAPI.deleteEntity).toHaveBeenCalledWith('bad'); expect(toastService.add).toHaveBeenCalledWith('error', 'Unable to delete: entity1.'); expect($scope.$emit).toHaveBeenCalledWith('custom_delete_event_failed', ['bad']); }); it('should raise passed and failed events only for deleted entities', function() { spyOn(toastService, 'add').and.callThrough(); spyOn(entityAPI, 'deleteEntity').and.callThrough(); spyOn($scope, '$emit').and.callThrough(); var entities = [ {name: 'bad_entity', id: 'bad'}, {name: 'entity2', id: '1'} ]; service.open($scope, entities, getContext()); $scope.$apply(); expect(entityAPI.deleteEntity).toHaveBeenCalledWith('bad'); expect(entityAPI.deleteEntity).toHaveBeenCalledWith('1'); expect(toastService.add).toHaveBeenCalledWith('success', 'Deleted : entity2.'); expect(toastService.add).toHaveBeenCalledWith('error', 'Unable to delete: bad_entity.'); expect($scope.$emit).toHaveBeenCalledWith('custom_delete_event_passed', ['1']); expect($scope.$emit).toHaveBeenCalledWith('custom_delete_event_failed', ['bad']); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/modal/modal.module.js0000664000567000056710000000165312701407063027012 0ustar jenkinsjenkins00000000000000/** * Copyright 2015 IBM Corp. * * 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'; /** * @ngdoc overview * @name horizon.framework.widgets.modal * * @description * The `horizon.framework.widgets.modal` provides modal services. * Requires {@link http://angular-ui.github.io/bootstrap/ `Angular-bootstrap`} */ angular .module('horizon.framework.widgets.modal', []); })(); horizon-9.0.0/horizon/static/framework/widgets/modal/simple-modal.html0000664000567000056710000000105012701407071027333 0ustar jenkinsjenkins00000000000000 horizon-9.0.0/horizon/static/framework/widgets/modal/delete-modal.service.js0000664000567000056710000001151312701407063030421 0ustar jenkinsjenkins00000000000000/** * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use self 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('horizon.framework.widgets.modal') .factory('horizon.framework.widgets.modal.deleteModalService', deleteModalService); deleteModalService.$inject = [ '$q', 'horizon.framework.widgets.modal.simple-modal.service', 'horizon.framework.widgets.toast.service', 'horizon.framework.util.q.extensions' ]; /** * @ngDoc factory * @name horizon.framework.widgets.modal.deleteModalService * * @Description * Brings up the delete confirmation modal dialog. * This provides a reusable modal service for deleting * entities. It can be used for deleting single or multiple * objects. * * It requires that the backing API Service allow for * suppressing of errors so that success and error messages * are shown only once when working with multiple objects. * * On submit, call the given deleteEntity method * and then raise the event. * On cancel, do nothing. */ function deleteModalService($q, simpleModalService, toast, $qExtensions) { var service = { open: open }; return service; ////////////// /** * @ngDoc method * @name horizon.framework.widgets.modal.deleteModalService.open * * @Description * Brings up the delete confirmation modal dialog for the given * set of entities. * * @param {Object} entities * The Entities that are to be deleted. * Each entity MUST have an ID field. They * could also have a name field which if present is * used in the confirmation display. If a name * is not provided, the ID will be displayed. * * @param {function} context.deleteEntity * The function that should be called to delete each entity. * The first argument is the id of the Entity to delete. * Note: This callback might need to suppress errors on the * alert service. * * @param {string} context.successEvent * The name of the event to emit for the entities that have been deleted successfully. * @param {string} context.failedEvent * The name of the event to emit when the entities that failed to delete successfully. * * On submit, delete given entities. * On cancel, do nothing. * * @return {Promise} From the opened modal. Resolves on modal submit, * rejects on modal cancel. * */ function open(scope, entities, context) { var options = { title: context.labels.title, body: interpolate(context.labels.message, [entities.map(getName).join("\", \"")]), submit: context.labels.submit }; return simpleModalService.modal(options).result.then(onModalSubmit); function onModalSubmit() { return $qExtensions.allSettled(entities.map(deleteEntityPromise)).then(notify); } function deleteEntityPromise(entity) { return {promise: context.deleteEntity(entity.id), context: entity}; } function notify(result) { if (result.pass.length > 0) { var passEntities = result.pass.map(getEntities); scope.$emit(context.successEvent, passEntities.map(getId)); toast.add('success', getMessage(context.labels.success, passEntities)); } if (result.fail.length > 0) { var failEntities = result.fail.map(getEntities); scope.$emit(context.failedEvent, failEntities.map(getId)); toast.add('error', getMessage(context.labels.error, failEntities)); } return { // Object intentionally left blank. This data is passed to // code that holds this action's promise. In the future, it may // contain entity IDs and types that were modified by this action. }; } } function getEntities(passResponse) { return passResponse.context; } /** * Helper method to get the displayed message */ function getMessage(message, entities) { return interpolate(message, [entities.map(getName).join(", ")]); } /** * Helper method to get the name of the entity */ function getName(entity) { return entity.name || entity.id; } /** * Helper method to get the id of the entity */ function getId(entity) { return entity.id; } } // end of deleteModalService })(); // end of IIFE horizon-9.0.0/horizon/static/framework/widgets/modal/simple-modal.spec.js0000664000567000056710000001167512701407063027753 0ustar jenkinsjenkins00000000000000/** * Copyright 2015 IBM Corp. * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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"; describe('horizon.framework.widgets.simple-modal', function() { beforeEach(module('horizon.framework')); describe('SimpleModalController', function() { var modalInstance, context, ctrl; beforeEach(inject(function($controller) { modalInstance = { close: function() {}, dismiss: function() {} }; context = { what: 'is it' }; ctrl = $controller('SimpleModalController', {$modalInstance: modalInstance, context: context}); })); it('establishes a controller', function() { expect(ctrl).toBeDefined(); }); it('sets context on the modalCtrl variable', function() { expect(ctrl.context).toBeDefined(); expect(ctrl.context).toEqual({ what: 'is it' }); }); it('sets action functions', function() { expect(ctrl.submit).toBeDefined(); expect(ctrl.cancel).toBeDefined(); }); it('makes submit close the modal instance', function() { expect(ctrl.submit).toBeDefined(); spyOn(modalInstance, 'close'); ctrl.submit(); expect(modalInstance.close.calls.count()).toBe(1); }); it('makes cancel close the modal instance', function() { expect(ctrl.cancel).toBeDefined(); spyOn(modalInstance, 'dismiss'); ctrl.cancel(); expect(modalInstance.dismiss).toHaveBeenCalledWith('cancel'); }); }); describe('horizon.framework.widgets.modal.simple-modal.service', function() { var service, modal; beforeEach(module(function($provide) { modal = { open: function() { return 'val'; }}; $provide.value('$modal', modal); })); beforeEach(inject(function($injector) { service = $injector.get('horizon.framework.widgets.modal.simple-modal.service'); })); it('defines the service', function() { expect(service).toBeDefined(); }); it('returns undefined if called with no parameters', function() { expect(service.modal()).toBeUndefined(); }); it('returns undefined if called without required parameters', function() { expect(service.modal({title: {}})).toBeUndefined(); expect(service.modal({body: {}})).toBeUndefined(); }); describe('Maximal Values Passed to the Modal', function() { var passed, passedContext; beforeEach(function() { var opts = { title: 'my title', body: 'my body', submit: 'Yes', cancel: 'No' }; spyOn(modal, 'open'); service.modal(opts); passed = modal.open.calls.argsFor(0)[0]; passedContext = passed.resolve.context(); }); it('sets the controller', function() { expect(passed.controller).toBe('SimpleModalController as modalCtrl'); }); it('sets the template URL', function() { expect(passed.templateUrl).toBe('/static/framework/widgets/modal/simple-modal.html'); }); it('sets the title', function() { expect(passedContext.title).toBe('my title'); }); it('sets the body', function() { expect(passedContext.body).toBe('my body'); }); it('sets the submit', function() { expect(passedContext.submit).toBe('Yes'); }); it('sets the cancel', function() { expect(passedContext.cancel).toBe('No'); }); }); describe('Minimal Values Passed to the Modal', function() { var passed, passedContext; beforeEach(function() { var opts = { title: 'my title', body: 'my body' }; spyOn(modal, 'open'); service.modal(opts); passed = modal.open.calls.argsFor(0)[0]; passedContext = passed.resolve.context(); }); it('sets the title', function() { expect(passedContext.title).toBe('my title'); }); it('sets the body', function() { expect(passedContext.body).toBe('my body'); }); it('defaults the submit', function() { expect(passedContext.submit).toBe('Submit'); }); it('defaults the cancel', function() { expect(passedContext.cancel).toBe('Cancel'); }); }); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/modal/wizard.controller.js0000664000567000056710000000326212701407063030112 0ustar jenkinsjenkins00000000000000/** * Copyright 2015 IBM Corp. * * 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('horizon.framework.widgets.modal') .controller('WizardModalController', WizardModalController); WizardModalController.$inject = [ '$modalInstance', '$scope', 'workflow', // modal injected 'submit' // modal injected ]; /** * @ngdoc controller * @name WizardModalController * @module horizon.framework.widgets.modal * @description * A modal controller for the wizard based workflows. * This controller is automatically included in WizardModalService. * This controller sets the modal actions and workflow on the given scope * as the Wizard needs them defined on the scope. */ function WizardModalController($modalInstance, $scope, workflow, submit) { /* eslint-disable angular/controller-as */ $scope.close = close; $scope.cancel = cancel; $scope.submit = submit; $scope.workflow = workflow; /* eslint-enable angular/controller-as */ function close(args) { $modalInstance.close(args); } function cancel() { $modalInstance.dismiss('cancel'); } } })(); horizon-9.0.0/horizon/static/framework/widgets/modal/wizard-modal.service.spec.js0000664000567000056710000000431712701407063031414 0ustar jenkinsjenkins00000000000000/** * 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"; describe('horizon.framework.widgets.wizard-modal.service', function() { var service, modal, $scope; beforeEach(module('horizon.framework')); beforeEach(module(function($provide) { modal = { open: function() {} }; $provide.value('$modal', modal); })); beforeEach(inject(function($injector, _$rootScope_) { $scope = _$rootScope_.$new(); service = $injector.get('horizon.framework.widgets.modal.wizard-modal.service'); })); it('defines the service', function() { expect(service).toBeDefined(); }); it('does not open the modal if called with no parameters', function() { spyOn(modal, 'open').and.callThrough(); service.modal(); expect(modal.open).not.toHaveBeenCalled(); }); it('should open the modal if called with required parameters', function() { spyOn(modal, 'open').and.callThrough(); service.modal({scope: $scope, workflow: {}, submit: {}}); expect(modal.open).toHaveBeenCalled(); }); it('should open the modal with correct parameters', function() { spyOn(modal, 'open').and.callThrough(); var workflow = {id: 'w'}; var submit = {id: 's'}; service.modal({scope: $scope, workflow: workflow, submit: submit}); expect(modal.open).toHaveBeenCalled(); var modalOpenArgs = modal.open.calls.argsFor(0)[0]; expect(modalOpenArgs.controller).toEqual('WizardModalController as modalCtrl'); expect(modalOpenArgs.scope).toEqual($scope); expect(modalOpenArgs.resolve.workflow()).toEqual(workflow); expect(modalOpenArgs.resolve.submit()).toEqual(submit); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/modal/simple-modal.controller.js0000664000567000056710000000316212701407063031174 0ustar jenkinsjenkins00000000000000/** * Copyright 2015 IBM Corp. * * 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'; /** * @ngdoc controller * @name SimpleModalController * * @param(object) scope of the controller * @param(object) modal instance from angular-bootstrap * @param(object) context object provided by the user * * @description * Horizon's controller for confirmation dialog. * Passes context along to the template. * If user presses cancel button or closes dialog, modal gets dismissed. * If user presses submit button, modal gets closed. * This controller is automatically included in modalService. */ angular .module('horizon.framework.widgets.modal') .controller('SimpleModalController', SimpleModalController); SimpleModalController.$inject = [ '$modalInstance', 'context' ]; function SimpleModalController($modalInstance, context) { var ctrl = this; ctrl.context = context; ctrl.submit = function() { $modalInstance.close(); }; ctrl.cancel = function() { $modalInstance.dismiss('cancel'); }; } // end of function })(); horizon-9.0.0/horizon/static/framework/widgets/modal/wizard.controller.spec.js0000664000567000056710000000345412701407063031046 0ustar jenkinsjenkins00000000000000/** * Copyright 2015 IBM Corp. * * 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'; var wizardCtrl = 'WizardModalController'; describe(wizardCtrl, function() { var modalCalls = [ 'close', 'dismiss' ]; var modalInstance = jasmine.createSpyObj('$modalInstance', modalCalls); var scope; ////////// beforeEach(module('horizon.framework.widgets.modal')); beforeEach(inject(function($controller, $rootScope) { scope = $rootScope.$new(); $controller(wizardCtrl, { $modalInstance: modalInstance, $scope: scope, workflow: { steps: 'somestep' }, submit: { api: 'someapi' } }); })); ////////// it('should inject and assign workflow and submit', injectAssign); it('should forward call to modalInstance on close', closeModal); it('should forward call to modalInstance on cancel', cancelModal); ////////// function injectAssign() { expect(scope.workflow.steps).toEqual('somestep'); expect(scope.submit.api).toEqual('someapi'); } function closeModal() { scope.close(); expect(modalInstance.close).toHaveBeenCalled(); } function cancelModal() { scope.cancel(); expect(modalInstance.dismiss).toHaveBeenCalledWith('cancel'); } }); })(); horizon-9.0.0/horizon/static/framework/widgets/modal/simple-modal.service.js0000664000567000056710000000520512701407063030451 0ustar jenkinsjenkins00000000000000/** * Copyright 2015 IBM Corp. * * 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'; /** * @ngdoc service * @name simpleModalService * * @description * Horizon's wrapper for angular-bootstrap modal service. * It should only be use for small confirmation dialogs. * @param {object} the object containing title, body, submit, and cancel labels * @param {object} the object returned from angular-bootstrap $modal * * @example: * angular * .controller('modalExampleCtrl', ExampleCtrl); * * ExampleCtrl.$inject = [ * '$scope', * 'horizon.framework.widgets.modal.simple-modal.service' * ]; * * function ExampleCtrl($scope, simpleModalService) { * var options = { * title: 'Confirm Delete', * body: 'Are you sure you want to delete this item?', * submit: 'Yes', * cancel: 'No' * }; * * simpleModalService.modal(options).result.then(function() { * // user clicked on submit button * // do something useful here * }); * }); */ angular .module('horizon.framework.widgets.modal') .factory('horizon.framework.widgets.modal.simple-modal.service', modalService); modalService.$inject = [ '$modal', 'horizon.framework.widgets.basePath', 'horizon.framework.util.i18n.gettext' ]; function modalService($modal, path, gettext) { var service = { modal: modal }; return service; //////////////////// function modal(params) { if (params && params.title && params.body) { var options = { controller: 'SimpleModalController as modalCtrl', templateUrl: path + 'modal/simple-modal.html', resolve: { context: function() { return { title: params.title, body: params.body, submit: params.submit || gettext('Submit'), cancel: params.cancel || gettext('Cancel') }; } } }; return $modal.open(options); } } // end of modalOptions function } // end of modalService function })(); horizon-9.0.0/horizon/static/framework/widgets/load-edit/0000775000567000056710000000000012701407231024632 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/load-edit/load-edit.module.js0000664000567000056710000000214012701407063030316 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc overview * @name horizon.framework.widgets.load-edit * @description * */ angular .module('horizon.framework.widgets.load-edit', []) .config(config); config.$inject = ['$provide', '$windowProvider']; function config($provide, $windowProvider) { var path = $windowProvider.$get().STATIC_URL + 'framework/widgets/load-edit/'; $provide.constant('horizon.framework.widgets.load-edit.basePath', path); } })(); horizon-9.0.0/horizon/static/framework/widgets/load-edit/load-edit.directive.spec.js0000664000567000056710000000656312701407063031755 0ustar jenkinsjenkins00000000000000/* * 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'; describe('load-edit directive', function () { var $compile, $scope, key, element, $q, readFileService; beforeEach(module('templates')); beforeEach(module('horizon.framework.widgets.load-edit')); beforeEach(module(function($provide) { readFileService = {}; $provide.provider('horizon.framework.util.file.file-reader', function () { this.$get = function () { return readFileService; }; }); })); beforeEach(inject(function ($injector) { $scope = $injector.get('$rootScope').$new(); $q = $injector.get('$q'); $compile = $injector.get('$compile'); key = 'inputKey'; element = $compile( '' )($scope); $scope.$apply(); })); describe('onTextAreaChange listener', function() { var textarea; beforeEach(function() { textarea = element.find('textarea'); }); it('should set scriptModified to true when textarea has content', function () { textarea.val('any value'); textarea.trigger('propertychange'); $scope.$apply(); expect(element.isolateScope().scriptModified).toBe(true); }); it('should set scriptModified to false when textarea has no content', function () { textarea.val(''); textarea.trigger('propertychange'); $scope.$apply(); expect(element.isolateScope().scriptModified).toBe(false); }); it('should set userInput to the value of the textarea', function() { textarea.val('user input'); textarea.trigger('input'); $scope.$apply(); expect(element.isolateScope().userInput[key]).toBe('user input'); }); }); describe('onFileLoadListener', function() { it('should set the value of textContent to the file contents', function(done) { var contentPromise = $q.defer(); readFileService.readTextFile = function() { return contentPromise.promise; }; var fileInput = element.find('input[type="file"]'); var e = jQuery.Event( "change", {originalEvent: {target: {files: [['hi']]}}}); fileInput.trigger(e); contentPromise.promise.then(function() { expect(element.isolateScope().textContent).toBe('the contents of a file'); done(); }); contentPromise.resolve('the contents of a file'); $scope.$apply(); }); it('should handle when no file is passed in', function() { var fileInput = element.find('input[type="file"]'); var e = jQuery.Event( "change", {originalEvent: {target: {files: []}}}); fileInput.trigger(e); expect(element.isolateScope().textContent).toBe(''); }); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/load-edit/load-edit.directive.js0000664000567000056710000000716712701407063031025 0ustar jenkinsjenkins00000000000000/* * Copyright 2015 Hewlett Packard Enterprise Development Company LP * * 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'; /** * @ngdoc directive * @name horizon.framework.widgets:loadEdit * @scope * @element * @description * The 'loadEdit' directive supports and validates size of the script entered * * @param {object} config * @param {object} userInput * @param {object} key * * See configuration.html for example usage. */ angular .module('horizon.framework.widgets.load-edit') .directive('loadEdit', loadEdit); loadEdit.$inject = [ '$timeout', 'horizon.framework.util.file.file-reader', 'horizon.framework.widgets.load-edit.basePath' ]; function loadEdit($timeout, fileReader, basePath) { var directive = { restrict: 'E', scope: { config: '=', userInput: '=', key: '@' }, link: link, templateUrl: basePath + 'load-edit.html' }; return directive; //////////////////// function link($scope, $element) { var textarea = $element.find('textarea'); var fileInput = $element.find('input[type="file"]'); $scope.textContent = ''; /* HTML5 file API is supported by IE10+, Chrome, FireFox and Safari (on Mac). * * If HTML5 file API is not supported by user's browser, remove the option * to upload a script via file upload. */ $scope.config.fileApiSupported = !!FileReader; /* Angular won't fire change events when the The script is larger than the maximum size
horizon-9.0.0/horizon/static/framework/widgets/transfer-table/0000775000567000056710000000000012701407231025701 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.directive.js0000664000567000056710000000634712701407063033142 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * Copyright 2015 IBM Corp. * * 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'; /** * @ngdoc directive * @name horizon.framework.widgets.transfer-table.directive:transferTable * @restrict E * * @param {object} trModel Table data model (required) * @param {object} helpText Help text (optional) * @param {object} limits Max allocation (optional, default: 1) * * @description * The `transferTable` directive generates two tables and allows the * transfer of rows between the two tables. Help text and maximum * allocation are configurable. The defaults for help text and limits * are described above (constants: helpContent and limits). * * Refer to transfer-table.controller.js for data model description. * Refer to transfer-table.example.html to see it use as a directive. * * Optional arguments for each row in table data model: * disabled - disables the allocate button in available table * warningMessage - the message to show in warning tooltip * warnings - show warning text and icon next to value in table cell */ angular .module('horizon.framework.widgets.transfer-table') .directive('transferTable', transferTable); transferTable.$inject = [ 'horizon.framework.widgets.basePath' ]; function transferTable(path) { return { controller: 'transferTableController', controllerAs: 'trCtrl', restrict: ' E', scope: true, transclude: true, templateUrl: path + 'transfer-table/transfer-table.html', link: link }; ////////////////////// function link(scope, element, attrs, ctrl, transclude) { var allocated = element.find('.transfer-allocated'); var available = element.find('.transfer-available'); if ('cloneContent' in attrs) { var allocatedScope = scope.$new(); allocatedScope.$displayedItems = ctrl.allocated.displayedItems; allocatedScope.$sourceItems = ctrl.allocated.sourceItems; allocatedScope.$isAllocatedTable = true; transclude(allocatedScope, function(clone) { allocated.append(clone.filter('table')); }); var availableScope = scope.$new(); availableScope.$displayedItems = ctrl.available.displayedItems; availableScope.$sourceItems = ctrl.available.sourceItems; availableScope.$isAvailableTable = true; transclude(availableScope, function(clone) { available.append(clone.filter('table')); }); } else { transclude(scope, function(clone) { allocated.append(clone.filter('allocated')); available.append(clone.filter('available')); }); } } } })(); horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.controller.js0000664000567000056710000001467512701407063033352 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * Copyright 2015 IBM Corp. * * 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('horizon.framework.widgets.transfer-table') .controller('transferTableController', TransferTableController); TransferTableController.$inject = [ '$scope', '$timeout', '$parse', '$attrs', '$log', 'horizon.framework.widgets.transfer-table.events', 'horizon.framework.widgets.transfer-table.helpText', 'horizon.framework.widgets.transfer-table.limits' ]; /** * @ngdoc controller * @name horizon.framework.widgets.transfer-table.controller:transferTableController * @description * The `transferTableController` controller provides functions for allocating * and deallocating to and from the 'allocated' array, respectively. * This controller can be accessed through `trCtrl`. * * The data model assumes four arrays: allocated, displayedAllocated, * available, and displayedAvailable. Smart-Table requires additional * 'displayed' arrays for sorting and re-ordering. Of these four arrays, only * allocated is required. The remaining arrays are populated for you if they * are not present. * * @example * ``` * var availableItems = [ * { id: 'u1', username: 'User 1', disabled: true, warnings: { username: 'Invalid!' } }, * { id: 'u2', username: 'User 2', disabled: true, warningMessage: 'Invalid!' }, * { id: 'u3', username: 'User 3' } * ]; * $scope.model = { available: availableItems }; * $scope.limits = { maxAllocation: -1 }; * ``` * For usage example, see the transfer-table.example.html file. */ function TransferTableController( $scope, $timeout, $parse, $attrs, $log, events, helpText, limits ) { var trModel = $parse($attrs.trModel)($scope); var trHelpText = $parse($attrs.helpText)($scope); var trLimits = $parse($attrs.limits)($scope); var ctrl = this; ctrl.allocate = allocate; ctrl.deallocate = deallocate; ctrl.toggleView = toggleView; ctrl.updateAllocated = updateAllocated; ctrl.numAllocated = numAllocated; ctrl.helpText = angular.extend({}, helpText, trHelpText); ctrl.limits = angular.extend({}, limits, trLimits); ctrl.numAvailable = numAvailable; ctrl.views = { allocated: true, available: true }; // if available transfer table is updated dynamically (e.g. based on a dropdown // selection like in Launch Instance Boot Source), we need to update our data accordingly var availableChangedWatcher = $scope.$on(events.AVAIL_CHANGED, onAvailChanged); $scope.$on('$destroy', function () { availableChangedWatcher(); }); init(trModel); ////////// function init(model) { if (!angular.isArray(model.available)) { $log.error('Available is not an array.'); } if (model.allocated && !angular.isArray(model.allocated)) { $log.error('Allocated is not an array.'); } ctrl.available = { sourceItems: model.available, displayedItems: model.displayedAvailable ? model.displayedAvailable : [] }; ctrl.allocated = { sourceItems: model.allocated ? model.allocated : [], displayedItems: model.displayedAllocated ? model.displayedAllocated : [] }; ctrl.allocatedIds = {}; markAllocatedItems(); $scope.$watchCollection(getAllocated, markAllocatedItems); } function getAllocated() { return ctrl.allocated.sourceItems; } function markAllocatedItems() { angular.forEach(ctrl.allocated.sourceItems, function flag(item) { ctrl.allocatedIds[item.id] = true; }); } function allocate(item) { // we currently don't have to check for item uniqueness // because we are using the ng-repeat track by // Add to allocated only if limit not reached if (ctrl.limits.maxAllocation < 0 || ctrl.limits.maxAllocation > ctrl.allocated.sourceItems.length) { ctrl.allocated.sourceItems.push(item); ctrl.allocatedIds[item.id] = true; // Swap out items if only one allocation allowed } else if (ctrl.limits.maxAllocation === 1) { var temp = ctrl.allocated.sourceItems.pop(); delete ctrl.allocatedIds[temp.id]; // When swapping out, Smart-Table $watch is // not detecting change so timeout is used as workaround. $timeout(function() { ctrl.allocated.sourceItems.push(item); ctrl.allocatedIds[item.id] = true; $scope.$apply(); }, 0, false); } } // move item from allocated to available function deallocate(item) { var index = ctrl.allocated.sourceItems.indexOf(item); if (index >= 0) { ctrl.allocated.sourceItems.splice(index, 1); delete ctrl.allocatedIds[item.id]; } } // update allocated when users re-order via drag-and-drop function updateAllocated(event, item, orderedItems) { ctrl.allocated.sourceItems.splice(0, ctrl.allocated.sourceItems.length); Array.prototype.push.apply(ctrl.allocated.sourceItems, orderedItems); } function onAvailChanged(e, args) { ctrl.available = { sourceItems: args.data.available, displayedItems: args.data.displayedAvailable ? args.data.displayedAvailable : [] }; for (var i = 0; i < ctrl.available.sourceItems.length; i++) { var item = ctrl.available.sourceItems[i]; if (item.id in ctrl.allocatedIds) { ctrl.allocated.sourceItems.splice(i, 1); delete ctrl.allocatedIds[item.id]; } } } ///////////// function toggleView(view) { ctrl.views[view] = !ctrl.views[view]; } function numAllocated() { return ctrl.allocated.sourceItems.length; } function numAvailable() { return ctrl.available.sourceItems.length - ctrl.allocated.sourceItems.length; } } })(); horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.controller.spec.js0000664000567000056710000002047112701407063034272 0ustar jenkinsjenkins00000000000000/* * Copyright 2015 IBM Corp. * * 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'; describe('transfer-table controller', function() { beforeEach(module('templates')); beforeEach(module('smart-table')); beforeEach(module('horizon.framework')); var log, params, scope; beforeEach(module(function($provide) { // we will mock scope and timeout in this test // because we aren't concern with rendering results var timeout = function(fn) { fn(); }; // we will mock parse and attrs // because we want to control the parameters log = { error: function() {} }; var attrs = angular.noop; var parse = function(attr) { return function() { return attr ? attr : {}; }; }; $provide.value('$timeout', timeout); $provide.value('$parse', parse); $provide.value('$attrs', attrs); $provide.value('$log', log); })); beforeEach(inject(function($injector, _$rootScope_) { scope = _$rootScope_.$new(); params = { '$scope': scope, '$timeout': $injector.get('$timeout'), '$parse': $injector.get('$parse'), '$attrs': $injector.get('$attrs'), '$log': $injector.get('$log'), 'helpText': {}, 'limits': { maxAllocation: 1 } }; })); function generateItems(count) { var itemList = []; for (var index = 0; index < count; index++) { itemList[index] = { id: index }; } return itemList; } ////////// describe('initialization code', function() { var controllerProvider; beforeEach(inject(function($controller) { controllerProvider = $controller; spyOn(log, 'error'); })); it('should log error on init with bad model', testBadModel); it('should initialize IDs with good model', testGoodModel); function testBadModel() { params.$attrs = { trModel: { available: 'abc', allocated: 123 } }; controllerProvider('transferTableController', params); expect(log.error).toHaveBeenCalled(); expect(log.error.calls.count()).toEqual(2); } function testGoodModel() { var availableCount = 10; var allocatedCount = 5; params.$attrs = { trModel: { available: generateItems(availableCount), allocated: generateItems(allocatedCount) } }; var trCtrl = controllerProvider('transferTableController', params); expect(log.error).not.toHaveBeenCalled(); expect(Object.keys(trCtrl.allocatedIds).length).toEqual(allocatedCount); expect(trCtrl.allocated.sourceItems.length).toEqual(allocatedCount); expect(trCtrl.available.sourceItems.length).toEqual(availableCount); } }); describe('core functions', function() { var trCtrl; beforeEach(inject(function($controller) { trCtrl = $controller('transferTableController', params); })); it('should always allocate if allocation limit is negative', testLimitNegative); it('should not allocate if allocation limit is reached', testLimitMaxed); it('should swap out allocated item if allocation limit is one', testLimitOne); it('should deallocate by moving item from allocated to available list', testDeallocate); it('should update allocated on reorder', testUpdateAllocated); it('should update allocatedIds if allocated change', testAllocatedIds); it('should toggle the views correctly on request', testToggleView); it('should refresh items if transferTableChanged is triggered', testTransferTableChanged); ////////// function testToggleView() { trCtrl.toggleView('allocated'); trCtrl.toggleView('available'); expect(trCtrl.views.allocated).toEqual(false); expect(trCtrl.views.available).toEqual(false); trCtrl.toggleView('allocated'); trCtrl.toggleView('available'); expect(trCtrl.views.allocated).toEqual(true); expect(trCtrl.views.available).toEqual(true); } function testLimitNegative() { var itemCount = 10; trCtrl.limits.maxAllocation = -1; trCtrl.available.sourceItems = generateItems(itemCount); for (var index = 0; index < itemCount; index++) { trCtrl.allocate(trCtrl.available.sourceItems[index]); } expect(Object.keys(trCtrl.allocatedIds).length).toEqual(itemCount); expect(trCtrl.allocated.sourceItems.length).toEqual(itemCount); expect(trCtrl.numAllocated()).toEqual(itemCount); expect(trCtrl.numAvailable()).toEqual(0); } function testLimitMaxed() { var itemCount = 10; trCtrl.limits.maxAllocation = 5; trCtrl.available.sourceItems = generateItems(itemCount); for (var index = 0; index < itemCount; index++) { trCtrl.allocate(trCtrl.available.sourceItems[index]); } expect(Object.keys(trCtrl.allocatedIds).length).toEqual(trCtrl.limits.maxAllocation); expect(trCtrl.allocated.sourceItems.length).toEqual(trCtrl.limits.maxAllocation); expect(trCtrl.numAllocated()).toEqual(trCtrl.limits.maxAllocation); expect(trCtrl.numAvailable()).toEqual(itemCount - trCtrl.limits.maxAllocation); } function testLimitOne() { var itemCount = 10; trCtrl.limits.maxAllocation = 1; trCtrl.available.sourceItems = generateItems(itemCount); for (var index = 0; index < itemCount; index++) { trCtrl.allocate(trCtrl.available.sourceItems[index]); } expect(Object.keys(trCtrl.allocatedIds).length).toEqual(trCtrl.limits.maxAllocation); expect(trCtrl.allocated.sourceItems.length).toEqual(trCtrl.limits.maxAllocation); expect(trCtrl.numAllocated()).toEqual(trCtrl.limits.maxAllocation); expect(trCtrl.numAvailable()).toEqual(itemCount - trCtrl.limits.maxAllocation); // when limit is one, we swap out items // the ID of the last item allocated should be present expect('9' in trCtrl.allocatedIds).toEqual(true); } function testDeallocate() { trCtrl.available.sourceItems = generateItems(1); var item = trCtrl.available.sourceItems[0]; trCtrl.allocate(item); trCtrl.deallocate(item); expect(item.id in trCtrl.allocatedIds).toEqual(false); expect(trCtrl.allocated.sourceItems.indexOf(item)).toEqual(-1); expect(trCtrl.numAllocated()).toEqual(0); expect(trCtrl.numAvailable()).toEqual(1); } function testAllocatedIds() { expect(trCtrl.allocatedIds).toEqual({}); trCtrl.allocated.sourceItems = [{id: 1}, {id: 2}]; scope.$apply(); expect(trCtrl.allocatedIds).toEqual({1: true, 2: true}); } function testUpdateAllocated() { var orderedItems = [1,2,3,4]; trCtrl.updateAllocated(null, null, orderedItems); expect(trCtrl.allocated.sourceItems).toEqual(orderedItems); expect(trCtrl.numAllocated()).toEqual(orderedItems.length); } function testTransferTableChanged() { var oldAvailableCount = 10; trCtrl.available.sourceItems = generateItems(oldAvailableCount); expect(trCtrl.available.sourceItems.length).toEqual(oldAvailableCount); var availableCount = 4; var newItems = { "data": { available: generateItems(availableCount) } }; spyOn(scope, '$broadcast').and.callThrough(); scope.$broadcast('horizon.framework.widgets.transfer-table.AVAIL_CHANGED', newItems); expect(scope.$broadcast).toHaveBeenCalledWith( 'horizon.framework.widgets.transfer-table.AVAIL_CHANGED', newItems); expect(trCtrl.available.sourceItems.length).toEqual(availableCount); } }); // end of core functions }); // end of transfer-table controller })(); // end of IIFE horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.module.js0000664000567000056710000000671212701407063032445 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc overview * @name horizon.framework.widgets.transfer-table * @description * * # horizon.framework.widgets.transfer-table * * The `horizon.framework.widgets.transfer-table` module provides support for transferring * rows between two tables (allocated and available). * * Requires {@link horizon.framework.widgets.table.directive:hzTable `hzTable`} module to * be installed. * * | Directives | * |--------------------------------------------------------------------------| * | {@link horizon.framework.widgets.transfer-table.directive:transferTable `transferTable`} | * */ angular .module('horizon.framework.widgets.transfer-table', []) /** * @ngdoc parameters * @name horizon.framework.widgets.transfer-table.constant:helpText * @param {string} allocTitle Title for allocation section * @param {string} availTitle Title for available section * @param {string} availHelpText Help text shown in available section * @param {string} noneAllocText Text shown if no allocated items * @param {string} noneAvailText Text shown if no available items * @param {string} allocHiddenText Text shown if allocated section hidden * @param {string} availHiddenText Text shown if available section hidden * @param {string} sectionToggleText Title for section toggle chevron icon * @param {string} orderText Title for drag and drop re-order icon * @param {string} expandDetailsText Title for expand icon */ .constant('horizon.framework.widgets.transfer-table.helpText', { allocTitle: gettext('Allocated'), availTitle: gettext('Available'), availHelpText: gettext('Select one'), noneAllocText: gettext('Select an item from Available items below'), noneAvailText: gettext('No available items'), allocHiddenText: gettext('Expand to see allocated items'), availHiddenText: gettext('Expand to see available items'), sectionToggleText: gettext('Click to show or hide'), orderText: gettext('Re-order items using drag and drop'), expandDetailsText: gettext('Click to see more details') }) /** * @ngdoc parameters * @name horizon.framework.widgets.transfer-table.constant:limits * @param {number} maxAllocation Maximum allocation allowed */ .constant('horizon.framework.widgets.transfer-table.limits', { maxAllocation: 1 }) .constant('horizon.framework.widgets.transfer-table.events', events()); /** * @ngdoc value * @name horizon.framework.widgets.transfer-table.events * @description a list of events for transfer tables */ function events() { return { AVAIL_CHANGED: 'horizon.framework.widgets.transfer-table.AVAIL_CHANGED' }; } })(); horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.module.spec.js0000664000567000056710000000157412701407063033377 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; describe('horizon.framework.widgets.transfer-table module', function() { it('should have been defined', function() { expect(angular.module('horizon.framework.widgets.transfer-table')).toBeDefined(); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.max-2.mock.html0000664000567000056710000000202112701407063033351 0ustar jenkinsjenkins00000000000000
Animal
{$ row.animal $} x
Animal
{$ alRow.animal $} x
horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.basic.mock.html0000664000567000056710000000203412701407063033512 0ustar jenkinsjenkins00000000000000
Animal
{$ row.animal $} x
Animal
{$ alRow.animal $} x
horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.example.html0000664000567000056710000000345012701407063033137 0ustar jenkinsjenkins00000000000000
Name description
No available items
{$ item.name $} {$ item.description $}
horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.html0000664000567000056710000000376412701407063031515 0ustar jenkinsjenkins00000000000000
{$ ::trCtrl.helpText.allocTitle $} {$ trCtrl.numAllocated() $} {$ ::trCtrl.helpText.allocHelpText $}
{$ ::trCtrl.helpText.allocHiddenText $}
{$ ::trCtrl.helpText.availTitle $} {$ trCtrl.numAvailable() $} {$ ::trCtrl.helpText.availHelpText $}
{$ ::trCtrl.helpText.availHiddenText $}
horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.directive.spec.js0000664000567000056710000001055712701407063034071 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * Copyright 2015 IBM Corp. * * 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'; describe('transfer-table directive', function() { beforeEach(module('templates')); beforeEach(module('smart-table')); beforeEach(module('horizon.framework')); var $templateCache, $compile, $scope, basePath; var available = [ { id: '1', animal: 'cat' }, { id: '2', animal: 'dog' }, { id: '3', animal: 'fish' } ]; beforeEach(inject(function($injector) { basePath = $injector.get('horizon.framework.widgets.basePath'); $templateCache = $injector.get('$templateCache'); $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); $scope.tableData = { available: available, allocated: [], displayedAvailable: [].concat(available), displayedAllocated: [] }; })); describe('render with allocated and available tags', function() { var $element; beforeEach(inject(function() { var path = 'transfer-table/transfer-table.basic.mock.html'; var markup = $templateCache.get(basePath + path); $element = angular.element(markup); $compile($element)($scope); $scope.$apply(); })); it('should place allocated and available element correctly', testContent); it('should have 0 allocated rows', testAllocatedRows); it('should have 3 available rows', testAvailableRows); /////////// function testContent() { expect($element.find('table').length).toBe(2); expect($element.find('.transfer-allocated > allocated').length).toBe(1); expect($element.find('.transfer-available > available').length).toBe(1); } function testAllocatedRows() { expect($element.find('.transfer-allocated tr[ng-repeat]').length).toBe(0); } function testAvailableRows() { expect($element.find('.transfer-available tr[ng-repeat]').length).toBe(3); } }); describe('clone content', function() { var $element; beforeEach(inject(function() { var path = 'transfer-table/transfer-table.clone.mock.html'; var markup = $templateCache.get(basePath + path); $element = angular.element(markup); $compile($element)($scope); $scope.$apply(); })); it('should contain 2 table elements', testCloneContent); it('should have 0 allocated rows', testAllocatedRows); it('should have 3 available rows', testAvailableRows); it('should create 2 new scopes', testNewScopes); /////////// function testCloneContent() { expect($element.find('table').length).toBe(2); expect($element.find('.transfer-allocated > table').length).toBe(1); expect($element.find('.transfer-available > table').length).toBe(1); } function testAllocatedRows() { expect($element.find('.transfer-allocated tr[ng-repeat]').length).toBe(0); } function testAvailableRows() { expect($element.find('.transfer-available tr[ng-repeat]').length).toBe(3); } function testNewScopes() { var allocatedScope = $element.find('.transfer-allocated > table').scope(); var availableScope = $element.find('.transfer-available > table').scope(); expect(allocatedScope.$isAllocatedTable).toBe(true); expect(allocatedScope.$sourceItems).toBe($scope.tableData.allocated); expect(allocatedScope.$displayedItems).toBe($scope.tableData.displayedAllocated); expect(availableScope.$isAvailableTable).toBe(true); expect(availableScope.$sourceItems).toBe($scope.tableData.available); expect(availableScope.$displayedItems).toBe($scope.tableData.displayedAvailable); } }); }); // end of transfer-table directive })(); // end of IIFE horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.max-1.mock.html0000664000567000056710000000206312701407063033356 0ustar jenkinsjenkins00000000000000
Animal
{$ alRow.animal $}
Animal
{$ row.animal $}
horizon-9.0.0/horizon/static/framework/widgets/transfer-table/transfer-table.clone.mock.html0000664000567000056710000000126012701407063033531 0ustar jenkinsjenkins00000000000000
ID Animal Action
{$ item.id $} {$ item.animal $} - +
horizon-9.0.0/horizon/static/framework/widgets/widgets.module.spec.js0000664000567000056710000000233712701407063027221 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; describe('horizon.framework.widgets', function () { it('should be defined', function () { expect(angular.module('horizon.framework.widgets')).toBeDefined(); }); }); describe('horizon.framework.widgets.basePath', function () { beforeEach(module('horizon.framework')); it('should be defined and set correctly', inject([ 'horizon.framework.widgets.basePath', '$window', function (basePath, $window) { expect(basePath).toBeDefined(); expect(basePath).toBe($window.STATIC_URL + 'framework/widgets/'); } ])); }); })(); horizon-9.0.0/horizon/static/framework/widgets/wizard/0000775000567000056710000000000012701407231024270 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/wizard/wizard.directive.js0000664000567000056710000000255012701407063030110 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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('horizon.framework.widgets.wizard') .directive('wizard', wizard); wizard.$inject = ['horizon.framework.widgets.basePath']; /** * @ngdoc directive * @name horizon.framework.widgets.wizard.directive:wizard * @description * The `wizard` directive allows you to create a multi-step process to accomplish a task. * Inside the wizard, you can have as many steps as you want. Each step acts like a form; * and the submit button will only show when all the steps are complete and valid. */ function wizard(basePath) { var directive = { controller: 'WizardController', templateUrl: basePath + 'wizard/wizard.html' }; return directive; } })(); horizon-9.0.0/horizon/static/framework/widgets/wizard/modal-container.controller.js0000664000567000056710000000277112701407063032076 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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('horizon.framework.widgets.wizard') .controller('ModalContainerController', ModalContainerController); ModalContainerController.$inject = ['$scope', '$modalInstance', 'launchContext']; /** * @ngdoc controller * @name horizon.framework.widgets.wizard.controller:ModalContainerController * @description * Extends the bootstrap-ui modal widget */ function ModalContainerController($scope, $modalInstance, launchContext) { // $scope is used because the methods are shared between // wizard and modal-container controller /*eslint-disable angular/controller-as */ $scope.launchContext = launchContext; $scope.close = function(args) { $modalInstance.close(args); }; $scope.cancel = function() { $modalInstance.dismiss(); }; /*eslint-enable angular/controller-as */ } })(); horizon-9.0.0/horizon/static/framework/widgets/wizard/wizard.module.js0000664000567000056710000000430112701407063027413 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /*eslint-disable max-len */ /** * @ngdoc overview * @name horizon.framework.widgets.wizard * @description * * # horizon.framework.widgets.wizard * * The `horizon.framework.widgets.wizard` module provides support for * creating a multi-step process to accomplish a task. * * Requires {@link horizon.framework.widgets.wizard.directive:wizard `wizard`} module to * be installed. * * | Directives | * |--------------------------------------------------------------------------| * | {@link horizon.framework.widgets.wizard.directive:wizard `wizard`} | * * | Controllers | * |---------------------------------------------------------------------------| * | {@link horizon.framework.widgets.wizard.controller:WizardController `WizardController`} | * | {@link horizon.framework.widgets.wizard.controller:ModalContainerController 'ModalContainerController'} | */ /*eslint-enable max-len */ angular .module('horizon.framework.widgets.wizard', []) .constant('horizon.framework.widgets.wizard.labels', { cancel: gettext('Cancel'), back: gettext('Back'), next: gettext('Next'), finish: gettext('Finish') }) .constant('horizon.framework.widgets.wizard.events', { ON_INIT_SUCCESS: 'ON_INIT_SUCCESS', ON_INIT_ERROR: 'ON_INIT_ERROR', ON_SWITCH: 'ON_SWITCH', BEFORE_SUBMIT: 'BEFORE_SUBMIT', AFTER_SUBMIT: 'AFTER_SUBMIT' }); })(); horizon-9.0.0/horizon/static/framework/widgets/wizard/wizard.html0000664000567000056710000000635412701407063026471 0ustar jenkinsjenkins00000000000000
horizon-9.0.0/horizon/static/framework/widgets/wizard/wizard.controller.js0000664000567000056710000001375012701407063030321 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; var extend = angular.extend; var forEach = angular.forEach; angular .module('horizon.framework.widgets.wizard') .controller('WizardController', WizardController); WizardController.$inject = [ '$scope', '$q', 'horizon.framework.widgets.wizard.labels', 'horizon.framework.widgets.wizard.events' ]; /** * @ngdoc controller * @name horizon.framework.widgets.wizard.controller:WizardController * @description * Controller used by 'wizard' */ function WizardController($scope, $q, wizardLabels, wizardEvents) { var viewModel = $scope.viewModel = {}; var initTask = $q.defer(); /*eslint-disable angular/controller-as */ $scope.initPromise = initTask.promise; $scope.currentIndex = -1; $scope.workflow = $scope.workflow || {}; if ($scope.workflow.initControllers) { $scope.workflow.initControllers($scope); } var steps = $scope.steps = $scope.workflow.steps || []; $scope.wizardForm = {}; $scope.switchTo = switchTo; $scope.showError = showError; /*eslint-enable angular/controller-as */ viewModel.btnText = extend({}, wizardLabels, $scope.workflow.btnText); viewModel.btnIcon = $scope.workflow.btnIcon || {}; viewModel.showSpinner = false; viewModel.hasError = false; viewModel.onClickFinishBtn = onClickFinishBtn; viewModel.isSubmitting = false; $scope.initPromise.then(onInitSuccess, onInitError); checkAllReadiness().then(always, always); ////////// function switchTo(index) { /** * In each step's controller, $scope.$index can be used by the step * to identify itself. For example: * * var comingToMe = (event.to === $scope.$index); */ $scope.$broadcast(wizardEvents.ON_SWITCH, { from: $scope.currentIndex, to: index }); /*eslint-disable angular/controller-as */ $scope.currentIndex = index; $scope.openHelp = false; /*eslint-enable angular/controller-as*/ } function showError(errorMessage) { viewModel.showSpinner = false; viewModel.errorMessage = errorMessage; viewModel.hasError = true; viewModel.isSubmitting = false; } function beforeSubmit() { $scope.$broadcast(wizardEvents.BEFORE_SUBMIT); } function afterSubmit(args) { $scope.$broadcast(wizardEvents.AFTER_SUBMIT); /*eslint-disable angular/controller-as */ $scope.close(args); /*eslint-enable angular/controller-as */ } function onClickFinishBtn() { // prevent the finish button from being clicked again viewModel.isSubmitting = true; beforeSubmit(); $scope.submit().then(afterSubmit, showError); } function onInitSuccess() { $scope.$broadcast(wizardEvents.ON_INIT_SUCCESS); } function onInitError() { $scope.$broadcast(wizardEvents.ON_INIT_ERROR); } /** * Each step in the workflow can provide an optional `checkReadiness` * method, which should return a promise. When this method is provided * with a step, the `.ready` property of the step will be set to * `false` until the promise gets resolved. If no `checkReadiness` method * is provided, the `.ready` property of the step will be set to `true` * by default. * * This is useful for workflows where some steps are optional and/or * displayed to the UI conditionally, and the check for the condition * is an asynchronous operation. * * @return {Promise} This promise gets resolved when all the checking * for each step's promises are done. * * @example ```js var launchInstanceWorkFlow = { //... steps: [ // ... { title: gettext('Network'), templateUrl: path + 'launch-instance/network/network.html', helpUrl: path + 'launch-instance/network/network.help.html', formName: 'launchInstanceNetworkForm', checkReadiness: function() { var d = $q.defer(); setTimeout(function() { d.resolve(); }, 500); return d.promise; } } //... ], //... }; ``` */ function checkAllReadiness() { var stepReadyPromises = []; forEach(steps, function(step, index) { step.ready = !step.checkReadiness; if (step.checkReadiness) { var promise = step.checkReadiness(); stepReadyPromises.push(promise); promise.then(function() { step.ready = true; }, function() { $scope.steps.splice(index, 1); } ); } }); viewModel.ready = (stepReadyPromises.length === 0); return $q.all(stepReadyPromises); } function switchToFirstReadyStep() { forEach(steps, function (step, index) { /*eslint-disable angular/controller-as */ if ($scope.currentIndex < 0 && step.ready) { $scope.currentIndex = index; return; } /*eslint-enable angular/controller-as */ }); } // angular promise doesn't have #always method right now, // this is a simple workaround. function always() { initTask.resolve(); viewModel.ready = true; switchToFirstReadyStep(); } } })(); horizon-9.0.0/horizon/static/framework/widgets/wizard/wizard.spec.js0000664000567000056710000002307712701407063027073 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; describe('horizon.framework.widgets.wizard module', function () { it('should have been defined', function () { expect(angular.module('horizon.framework.widgets.wizard')).toBeDefined(); }); }); describe('wizard directive', function () { var $compile, $scope, $q, element; beforeEach(module('templates')); beforeEach(module('horizon.framework')); beforeEach(inject(function ($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); $q = $injector.get('$q'); element = $compile('')($scope); })); it('should be compiled', function () { var element = $compile('some text')($scope); $scope.$apply(); expect(element.html().trim()).not.toBe('some text'); }); it('should have empty title by default', function () { $scope.workflow = {}; $scope.$apply(); expect(element[0].querySelector('.h4').textContent).toBe(''); }); it('should have title if it is specified by workflow', function () { var titleText = 'Some title'; $scope.workflow = {}; $scope.workflow.title = titleText; $scope.$apply(); expect(element[0].querySelector('.h4').textContent).toBe(titleText); }); it('should contain one help-panel', function () { $scope.workflow = {}; $scope.workflow.title = "doesn't matter"; $scope.$apply(); expect(element[0].querySelectorAll('#help-panel').length).toBe(1); }); it('should have no steps if no steps defined', function () { $scope.workflow = {}; $scope.$apply(); expect(element[0].querySelectorAll('.step').length).toBe(0); }); it('should have 3 steps if 3 steps defined', function () { $scope.workflow = { steps: [ {}, {}, {} ] }; $scope.$apply(); expect(element[0].querySelectorAll('.step').length).toBe(3); }); it('should have no nav items if no steps defined', function () { $scope.workflow = {}; $scope.$apply(); expect(element[0].querySelectorAll('.nav-item').length).toBe(0); }); it('should have 3 nav items if 3 steps defined', function () { $scope.workflow = { steps: [ {}, {}, {} ] }; $scope.$apply(); expect(element[0].querySelectorAll('.nav-item').length).toBe(3); }); it('should navigate correctly', function () { $scope.workflow = { steps: [ {}, {}, {} ] }; $scope.$apply(); expect($scope.currentIndex).toBe(0); expect(angular.element(element).find('.step').eq(0).hasClass('ng-hide')).toBe(false); expect(angular.element(element).find('.step').eq(1).hasClass('ng-hide')).toBe(true); expect(angular.element(element).find('.step').eq(2).hasClass('ng-hide')).toBe(true); expect(angular.element(element).find('.nav-item').eq(0).hasClass('active')).toBe(true); expect(angular.element(element).find('.nav-item').eq(1).hasClass('active')).toBe(false); expect(angular.element(element).find('.nav-item').eq(2).hasClass('active')).toBe(false); $scope.switchTo(1); $scope.$apply(); expect($scope.currentIndex).toBe(1); expect(angular.element(element).find('.step').eq(0).hasClass('ng-hide')).toBe(true); expect(angular.element(element).find('.step').eq(1).hasClass('ng-hide')).toBe(false); expect(angular.element(element).find('.step').eq(2).hasClass('ng-hide')).toBe(true); expect(angular.element(element).find('.nav-item').eq(0).hasClass('active')).toBe(false); expect(angular.element(element).find('.nav-item').eq(1).hasClass('active')).toBe(true); expect(angular.element(element).find('.nav-item').eq(2).hasClass('active')).toBe(false); $scope.switchTo(2); $scope.$apply(); expect($scope.currentIndex).toBe(2); expect(angular.element(element).find('.step').eq(0).hasClass('ng-hide')).toBe(true); expect(angular.element(element).find('.step').eq(1).hasClass('ng-hide')).toBe(true); expect(angular.element(element).find('.step').eq(2).hasClass('ng-hide')).toBe(false); expect(angular.element(element).find('.nav-item').eq(0).hasClass('active')).toBe(false); expect(angular.element(element).find('.nav-item').eq(1).hasClass('active')).toBe(false); expect(angular.element(element).find('.nav-item').eq(2).hasClass('active')).toBe(true); }); it('should disable back button in step 1/3', function () { $scope.workflow = { steps: [{}, {}, {}] }; $scope.$apply(); expect(element[0].querySelector('button.back').hasAttribute('disabled')).toBe(true); expect(element[0].querySelector('button.next').hasAttribute('disabled')).toBe(false); }); it('should show both back and next button in step 2/3', function () { $scope.workflow = { steps: [{}, {}, {}] }; $scope.$apply(); $scope.switchTo(1); $scope.$apply(); expect(element[0].querySelector('button.back').hasAttribute('disabled')).toBe(false); expect(element[0].querySelector('button.next').hasAttribute('disabled')).toBe(false); }); it('should disable next button in step 3/3', function () { $scope.workflow = { steps: [{}, {}, {}] }; $scope.$apply(); $scope.switchTo(2); $scope.$apply(); expect(element[0].querySelector('button.back').hasAttribute('disabled')).toBe(false); expect(element[0].querySelector('button.next').hasAttribute('disabled')).toBe(true); }); it('should have finish button disabled if wizardForm is invalid', function () { $scope.wizardForm = { }; $scope.$apply(); $scope.wizardForm.$invalid = true; $scope.$apply(); expect(element[0].querySelector('button.finish').hasAttribute('disabled')).toBe(true); }); it('should have finish button enabled if wizardForm is valid', function () { $scope.wizardForm = { }; $scope.$apply(); $scope.wizardForm.$invalid = false; $scope.$apply(); expect(element[0].querySelector('button.finish').hasAttribute('disabled')).toBe(false); }); it('should have finish button disabled if isSubmitting is set', function () { $scope.viewModel = { }; $scope.$apply(); $scope.viewModel.isSubmitting = true; $scope.$apply(); expect(element[0].querySelector('button.finish').hasAttribute('disabled')).toBe(true); }); it('should show error message after calling method showError', function () { var errorMessage = 'some error message'; $scope.$apply(); $scope.showError(errorMessage); $scope.$apply(); expect(element[0].querySelector('.error-message').textContent).toBe(errorMessage); }); it("checks steps' readiness", function() { var checkedStep = { checkReadiness: function() { return true; } }; $scope.workflow = { steps: [{}, checkedStep, {}] }; spyOn(checkedStep, 'checkReadiness').and.returnValue({then: function() {}}); $scope.$apply(); expect(checkedStep.checkReadiness).toHaveBeenCalled(); }); it('should pass result of submit function on to close function', function () { $scope.$apply(); $scope.submit = function() { var deferred = $q.defer(); deferred.resolve('foo'); return deferred.promise; }; $scope.close = angular.noop; spyOn($scope, 'close'); element[0].querySelector('button.finish').click(); expect($scope.close).toHaveBeenCalledWith('foo'); }); }); describe("ModalContainerController", function() { var ctrl, scope, modalInstance, launchContext; beforeEach(module('horizon.framework')); beforeEach(inject(function($controller) { scope = {}; modalInstance = { close: angular.noop, dismiss: angular.noop }; launchContext = { my: 'data' }; ctrl = $controller('ModalContainerController', { $scope: scope, $modalInstance: modalInstance, launchContext: launchContext } ); })); it('is defined', function() { expect(ctrl).toBeDefined(); }); it('sets scope.launchContext', function() { expect(scope.launchContext).toBeDefined(); expect(scope.launchContext).toEqual({ my: 'data' }); }); it('sets scope.close to a function that closes the modal', function() { expect(scope.close).toBeDefined(); spyOn(modalInstance, 'close'); scope.close(); expect(modalInstance.close).toHaveBeenCalled(); }); it('passes arguments to scope.close on to the modal close function', function() { spyOn(modalInstance, 'close'); scope.close('foo'); expect(modalInstance.close).toHaveBeenCalledWith('foo'); }); it('sets scope.cancel to a function that dismisses the modal', function() { expect(scope.cancel).toBeDefined(); spyOn(modalInstance, 'dismiss'); scope.cancel(); expect(modalInstance.dismiss).toHaveBeenCalled(); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/toast/0000775000567000056710000000000012701407231024122 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/toast/toast.service.js0000664000567000056710000000704512701407063027262 0ustar jenkinsjenkins00000000000000/* * Copyright 2015 IBM Corp. * * 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'; /** * @ngdoc service * @name toastService * * @description * This service can be used to display user messages, toasts, in Horizon. * To create a new toast, inject the 'horizon.framework.widgets.toast.service' * module into your current module. Then, use the service methods. * * For example to add a 'success' message: * toastService.add('success', 'User successfully created.'); * * All actions (add, clearAll, etc.) taken on the data are automatically * sync-ed with the HTML. */ angular .module('horizon.framework.widgets.toast') .factory('horizon.framework.widgets.toast.service', toastService); toastService.$inject = ['$timeout', 'horizon.framework.conf.toastOptions']; function toastService($timeout, toastOptions) { var toasts = []; var service = { types: {}, add: add, get: get, cancel: cancel, clearAll: clearAll, clearErrors: clearErrors, clearSuccesses: clearSuccesses }; /** * There are 5 types of toasts, which are based off Bootstrap alerts. */ service.types = { danger: gettext('Danger'), warning: gettext('Warning'), info: gettext('Info'), success: gettext('Success'), error: gettext('Error') }; return service; /////////////////////// /** * Helper method used to remove all the toasts matching the 'type' * passed in. */ function clear(type) { for (var i = toasts.length - 1; i >= 0; i--) { if (toasts[i].type === type) { toasts.splice(i, 1); } } } function autoDismiss(toast) { $timeout(function dismiss() { var index = toasts.indexOf(toast); var dismissible = toastOptions.dimissible.indexOf('alert-' + toast.type); // check if the toast exists and if it is dismissible (by checking // the toastOptions config), then we remove it after a delay if (index > -1 && dismissible > -1) { toasts.splice(index, 1); } }, toastOptions.delay); } /** * Remove a single toast. */ function cancel(index) { toasts.splice(index, 1); } /** * Create a toast object and push it to the toasts array. */ function add(type, msg) { var toast = { type: type === 'error' ? 'danger' : type, typeMsg: this.types[type], msg: msg, cancel: cancel }; autoDismiss(toast); toasts.push(toast); } /** * Return all toasts. */ function get() { return toasts; } /** * Remove all toasts. */ function clearAll() { toasts = []; } /** * Remove all toasts of type 'danger.' */ function clearErrors() { clear('danger'); } /** * Remove all toasts of type 'success.' */ function clearSuccesses() { clear('success'); } } })(); horizon-9.0.0/horizon/static/framework/widgets/toast/toast.spec.js0000664000567000056710000001266612701407063026561 0ustar jenkinsjenkins00000000000000/* * Copyright 2015 IBM Corp * * 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'; describe('horizon.framework.widgets.toast module', function() { it('should have been defined', function () { expect(angular.module('horizon.framework.widgets.toast')).toBeDefined(); }); }); describe('toast factory', function() { var $timeout, service; var successMsg = "I am success."; var dangerMsg = "I am danger."; var infoMsg = "I am info."; var errorMsg = "I am error."; beforeEach(module('templates')); beforeEach(module('horizon.framework')); beforeEach(inject(function ($injector) { service = $injector.get('horizon.framework.widgets.toast.service'); $timeout = $injector.get('$timeout'); })); it('should create different toasts', function() { service.add('danger', dangerMsg); expect(service.get().length).toBe(1); expect(service.get()[0].type).toBe('danger'); service.add('success', successMsg); expect(service.get().length).toBe(2); expect(service.get()[1].type).toBe('success'); service.add('info', infoMsg); expect(service.get().length).toBe(3); expect(service.get()[2].msg).toBe(infoMsg); service.add('error', errorMsg); expect(service.get().length).toBe(4); expect(service.get()[3].type).toBe('danger'); expect(service.get()[3].msg).toBe(errorMsg); }); it('should dismiss specific toasts after a delay', function() { service.add('danger', dangerMsg); service.add('success', successMsg); service.add('info', infoMsg); expect(service.get().length).toBe(3); $timeout.flush(); expect(service.get().length).toBe(1); expect(service.get()[0].type).toBe('danger'); }); it('should provide a function to clear all toasts', function() { service.add('success', successMsg); service.add('success', successMsg); service.add('info', infoMsg); expect(service.get().length).toBe(3); service.clearAll(); expect(service.get().length).toBe(0); }); it('should provide a function to clear all error toasts', function() { service.add('danger', dangerMsg); service.add('success', successMsg); service.add('danger', dangerMsg); service.add('error', errorMsg); expect(service.get().length).toBe(4); service.clearErrors(); expect(service.get().length).toBe(1); expect(service.get()[0].type).toBe('success'); }); it('should provide a function to clear all success toasts', function() { service.add('success', successMsg); service.add('success', successMsg); service.add('info', infoMsg); expect(service.get().length).toBe(3); service.clearSuccesses(); expect(service.get().length).toBe(1); expect(service.get()[0].type).toBe('info'); }); it('should provide a function to clear a specific toast', function() { service.add('success', successMsg); service.add('info', infoMsg); service.cancel(1); expect(service.get().length).toBe(1); expect(service.get()[0].type).not.toEqual('info'); }); }); describe('toast directive', function () { var $compile, $scope, $element, service; var successMsg = "I am success."; var dangerMsg = "I am danger."; var infoMsg = "I am info."; var errorMsg = "I am error."; function toasts() { return $element.find('.alert'); } beforeEach(module('templates')); beforeEach(module('horizon.framework')); beforeEach(inject(function ($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); service = $injector.get('horizon.framework.widgets.toast.service'); var markup = ''; $element = $compile(markup)($scope); $scope.$apply(); })); it('should create toasts using ng-repeat', function() { service.add('danger', dangerMsg); service.add('success', successMsg); service.add('info', infoMsg); service.add('error', errorMsg); $scope.$apply(); expect(toasts().length).toBe(4); }); it('should have the proper classes for different toasts types', function() { service.add('danger', dangerMsg); service.add('success', successMsg); service.add('info', infoMsg); service.add('error', errorMsg); $scope.$apply(); expect(toasts().length).toBe(4); expect(toasts().eq(0).hasClass('alert-danger')); expect(toasts().eq(1).hasClass('alert-success')); expect(toasts().eq(2).hasClass('alert-info')); expect(toasts().eq(3).hasClass('alert-danger')); }); it('should be possible to remove a toast by clicking close', function() { service.add('success', successMsg); $scope.$apply(); expect(toasts().length).toBe(1); toasts().eq(0).find('.close').click(); $scope.$apply(); expect(toasts().length).toBe(0); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/toast/toast.module.js0000664000567000056710000000300212701407063027074 0ustar jenkinsjenkins00000000000000/* * Copyright 2015 IBM Corp. * * 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'; /** * @ngdoc overview * @name horizon.framework.widgets.toast * description * * # horizon.framework.widgets.toast * * The `horizon.framework.widgets.toast` module provides pop-up notifications to Horizon. * A toast is a short text message triggered by a user action to provide * real-time information. Toasts do not disrupt the page's behavior and * typically auto-expire and fade. Also, toasts do not accept any user * interaction. * * * | Components | * |--------------------------------------------------------------------------| * | {@link horizon.framework.widgets.toast.factory:toastService `toastService`} | * | {@link horizon.framework.widgets.toast.directive:toast `toast`} | * */ angular .module('horizon.framework.widgets.toast', []); })(); horizon-9.0.0/horizon/static/framework/widgets/toast/toast.directive.js0000664000567000056710000000262012701407063027572 0ustar jenkinsjenkins00000000000000/* * Copyright 2015 IBM Corp. * * 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'; /** * @ngdoc directive * @name horizon.framework.widgets.toast.directive:toast * * @description * The `toast` directive allows you to place the toasts wherever you * want in your layout. Currently styling is pulled from Bootstrap alerts. * * @restrict EA * @scope true * */ angular .module('horizon.framework.widgets.toast') .directive('toast', toast); toast.$inject = ['horizon.framework.widgets.toast.service', 'horizon.framework.widgets.basePath']; function toast(toastService, path) { var directive = { restrict: 'EA', templateUrl: path + 'toast/toast.html', scope: {}, link: link }; return directive; function link(scope) { scope.toast = toastService; } } })(); horizon-9.0.0/horizon/static/framework/widgets/toast/toast.html0000664000567000056710000000040612701407063026145 0ustar jenkinsjenkins00000000000000
{$ toast.typeMsg $}: {$ toast.msg $}
horizon-9.0.0/horizon/static/framework/widgets/widgets.module.js0000664000567000056710000000166412701407063026272 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; angular .module('horizon.framework.widgets', [ 'horizon.framework.widgets.headers', 'horizon.framework.widgets.help-panel', 'horizon.framework.widgets.wizard', 'horizon.framework.widgets.table', 'horizon.framework.widgets.modal', 'horizon.framework.widgets.modal-wait-spinner', 'horizon.framework.widgets.transfer-table', 'horizon.framework.widgets.charts', 'horizon.framework.widgets.action-list', 'horizon.framework.widgets.metadata', 'horizon.framework.widgets.toast', 'horizon.framework.widgets.magic-search', 'horizon.framework.widgets.load-edit' ]) .config(config); config.$inject = ['$provide', '$windowProvider']; function config($provide, $windowProvider) { var path = $windowProvider.$get().STATIC_URL + 'framework/widgets/'; $provide.constant('horizon.framework.widgets.basePath', path); } })(); horizon-9.0.0/horizon/static/framework/widgets/modal-wait-spinner/0000775000567000056710000000000012701407231026502 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/modal-wait-spinner/modal-wait-spinner.directive.js0000664000567000056710000000371712701407063034542 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; /* * @ngdoc directive * @name horizon.framework.widgets.modal-wait-spinner.directive:waitSpinner * @description * A "global" wait spinner that displays a line of text followed by "...". * * Requires {@link http://angular-ui.github.io/bootstrap/ `Angular-bootstrap`} * * Used when the user must wait before any additional action is possible. Can be * launched from modal dialogs. * * @example * *
   *    angular
   *      .controller('MyController', MyController);
   *
   *    MyController.$inject = [
   *      '$scope',
   *      'horizon.framework.widgets.modal-wait-spinner.service'
   *    ];
   *
   *    function MyController($scope, modalWaitSpinnerService) {
   *      $scope.showSpinner = function () {
   *        modalWaitSpinnerService.showModalSpinner(gettext("Loading"));
   *      }
   *
   *      $scope.hideSpinner = function () {
   *        modalWaitSpinnerService.hideModalSpinner();
   *      }
   *    }
   *   
*
*/ angular .module('horizon.framework.widgets.modal-wait-spinner') .directive('waitSpinner', waitSpinner); waitSpinner.$inject = ['horizon.framework.conf.spinner_options']; function waitSpinner(spinnerOptions) { var directive = { scope: { text: '@text' // One-direction binding (reads from parent) }, restrict: 'A', link: link, template: '

{$text$}…

' }; return directive; //////////////////// /* * At the time link is executed, element may not have been sized by the browser. * Spin.js may mistakenly places the spinner at 50% of 0 (left:0, top:0). To work around * this, explicitly set 50% left and top to center the spinner in the parent * container */ function link(scope, element) { element.spin(spinnerOptions.modal); element.find('.spinner').css({'left': '50%', 'top': '50%'}); } } })(); horizon-9.0.0/horizon/static/framework/widgets/modal-wait-spinner/modal-wait-spinner.module.js0000664000567000056710000000216212701407063034042 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /* * @ngdoc overview * @name horizon.framework.widgets.modal-wait-spinner * @description * A "global" wait spinner that displays a line of text followed by "...". * * Requires {@link http://angular-ui.github.io/bootstrap/ `Angular-bootstrap`} * * Used when the user must wait before any additional action is possible. Can be * launched from modal dialogs. */ angular .module('horizon.framework.widgets.modal-wait-spinner', []); })(); horizon-9.0.0/horizon/static/framework/widgets/modal-wait-spinner/modal-wait-spinner.service.js0000664000567000056710000000335612701407063034223 0ustar jenkinsjenkins00000000000000(function () { 'use strict'; angular .module('horizon.framework.widgets.modal-wait-spinner') .factory('horizon.framework.widgets.modal-wait-spinner.service', WaitSpinnerService); WaitSpinnerService.$inject = ['$modal']; /* * @ngdoc factory * @name horizon.framework.widgets.modal-wait-spinner.factory:WaitSpinnerService * @description * In order to provide a seamless transition to a Horizon that uses more Angular * based pages, the service is currently implemented using the existing * Spin.js library and the corresponding JQuery plugin (jquery.spin.js). This widget * looks and feels the same as the existing spinner we are familiar with in Horizon. * Over time, uses of the existing Horizon spinner ( horizon.modals.modal_spinner() ) * can be phased out, or refactored to use this component. */ function WaitSpinnerService ($modal) { var spinner = this; var service = { showModalSpinner: showModalSpinner, hideModalSpinner: hideModalSpinner }; return service; //////////////////// function showModalSpinner(spinnerText) { var modalOptions = { backdrop: 'static', /* * Using
for wait-spinner instead of a wait-spinner element * because the existing Horizon spinner CSS styling expects a div * for the modal-body */ template: '', windowClass: 'modal-wait-spinner modal_wrapper loading' }; spinner.modalInstance = $modal.open(modalOptions); } function hideModalSpinner() { if (spinner.modalInstance) { spinner.modalInstance.dismiss(); delete spinner.modalInstance; } } } })(); horizon-9.0.0/horizon/static/framework/widgets/modal-wait-spinner/modal-wait-spinner.spec.js0000664000567000056710000000553612701407063033517 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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"; describe('Wait Spinner Tests', function() { var service; beforeEach(module('ui.bootstrap')); beforeEach(module('horizon.framework')); beforeEach(inject(function($injector) { service = $injector.get('horizon.framework.widgets.modal-wait-spinner.service'); })); it('returns the service', function() { expect(service).toBeDefined(); }); describe('showModalSpinner', function() { it('is defined', function() { expect(service.showModalSpinner).toBeDefined(); }); it('opens modal with the correct object', inject(function($modal) { var wanted = { backdrop: 'static', template: '', windowClass: 'modal-wait-spinner modal_wrapper loading' }; spyOn($modal, 'open'); service.showModalSpinner('my text'); expect($modal.open).toHaveBeenCalled(); expect($modal.open.calls.count()).toBe(1); expect($modal.open.calls.argsFor(0)).toEqual([wanted]); })); }); describe('hideModalSpinner', function() { it('has hideModalSpinner', function() { expect(service.hideModalSpinner).toBeDefined(); }); it("dismisses modal when it has been opened", inject(function($modal) { var modal = {dismiss: function() {}}; spyOn($modal, 'open').and.returnValue(modal); service.showModalSpinner('asdf'); spyOn(modal, 'dismiss'); service.hideModalSpinner(); expect(modal.dismiss).toHaveBeenCalled(); })); }); }); describe('Wait Spinner Directive', function() { var $scope, $element; beforeEach(module('ui.bootstrap')); beforeEach(module('horizon.framework')); beforeEach(inject(function($injector) { var $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); var markup = '
'; $element = angular.element(markup); $compile($element)($scope); $scope.$apply(); })); it("creates a p element", function() { var elems = $element.find('p'); expect(elems.length).toBe(1); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/modal-wait-spinner/modal-wait-spinner.scss0000664000567000056710000000146012701407063033115 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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. */ /* * Disable the Angular Bootstrap slide in animation for wait spinner modals */ .modal-wait-spinner.modal.fade .modal-dialog, .modal.in .modal-dialog { @include translate(0, 0); }horizon-9.0.0/horizon/static/framework/widgets/charts/0000775000567000056710000000000012701407231024254 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/charts/chart-tooltip.scss0000664000567000056710000000134512701407063027750 0ustar jenkinsjenkins00000000000000.chart-tooltip { background-color: $tooltip-bg; display: none; padding: $tooltip-padding; position: absolute; white-space: nowrap; z-index: $zindex-popover; &.tooltip-enabled { display: inline-block; } .tooltip-key { color: $tooltip-color; } .tooltip-value { color: $tooltip-color; } span.fa { fill: none; &.usage { color: $brand-primary; } &.added { color: lighten($brand-primary, 20%); } &.remaining { color: $gray-lighter; } &.danger { color: $brand-danger; } } } // styles for donut chart tooltips over quota .pie-chart.danger { .chart-tooltip { span.fa { &.added { color: $brand-danger; } } } } horizon-9.0.0/horizon/static/framework/widgets/charts/charts.spec.js0000664000567000056710000000271512701407063027037 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; describe('horizon.framework.widgets.charts module', function () { it('should be defined', function () { expect(angular.module('horizon.framework.widgets.charts')).toBeDefined(); }); describe('showKeyFilter', function () { var showKeyFilter; beforeEach(module('horizon.framework')); beforeEach(inject(function (_showKeyFilterFilter_) { showKeyFilter = _showKeyFilterFilter_; })); it('should filter keys based on hideKey attribute', function () { var someData = [{}, {hideKey: true}, {hideKey: false}]; var expectedData = [{}, {hideKey: false}]; expect(showKeyFilter(someData)).toEqual(expectedData); }); it('should accept empty arrays', function () { expect(showKeyFilter([])).toEqual([]); }); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/charts/pie-chart.spec.js0000664000567000056710000002323512701407063027427 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; describe('horizon.framework.widgets.charts module', function () { it('should be defined', function () { expect(angular.module('horizon.framework.widgets.charts')).toBeDefined(); }); }); describe('pie chart directive', function () { var $scope, $elementMax, $elementTotal, $elementOverMax, $elementNoQuota, quotaChartDefaults; beforeEach(module('templates')); beforeEach(module('horizon.framework')); function cleanSpaces(string) { return string.trim().replace(/\s+/, ' '); } beforeEach(inject(function ($injector) { var $compile = $injector.get('$compile'); $scope = $injector.get('$rootScope').$new(); quotaChartDefaults = $injector.get('horizon.framework.widgets.charts.quotaChartDefaults'); $scope.testDataTotal = { title: 'Total Instances', label: '25%', data: [ { label: quotaChartDefaults.usageLabel, value: 1, colorClass: quotaChartDefaults.usageColorClass }, { label: quotaChartDefaults.addedLabel, value: 1, colorClass: quotaChartDefaults.addedColorClass }, { label: quotaChartDefaults.remainingLabel, value: 6, colorClass: quotaChartDefaults.remainingColorClass, hideKey: true } ] }; $scope.testDataMax = {}; $scope.testDataOverMax = {}; $scope.testDataNoQuota = {}; // Max chart is similar to Total chart data structure // but has an additional 'maxLimit' property angular.copy($scope.testDataTotal, $scope.testDataMax); $scope.testDataMax.maxLimit = 8; // using the Max chart, assign values to test for overMax angular.copy($scope.testDataMax, $scope.testDataOverMax); $scope.testDataOverMax.data[0].value = 6; $scope.testDataOverMax.data[1].value = 3; $scope.testDataOverMax.data[2].value = 0; $scope.testDataOverMax.overMax = true; angular.copy($scope.testDataMax, $scope.testDataNoQuota); $scope.testDataNoQuota.maxLimit = Infinity; $scope.chartSettings = { innerRadius: 24, outerRadius: 30, titleClass: 'pie-chart-title-medium', showTitle: true, showLabel: true, showLegend: true, tooltipIcon: 'fa-square' }; // Max/quota chart markup var markupMax = '' + ''; $elementMax = angular.element(markupMax); $compile($elementMax)($scope); // Over Max/quota chart markup var markupOverMax = '' + ''; $elementOverMax = angular.element(markupOverMax); $compile($elementOverMax)($scope); // Total chart markup var markupTotal = '' + ''; $elementTotal = angular.element(markupTotal); $compile($elementTotal)($scope); // Unlimited quota chart markup var markupNoQuota = '' + ''; $elementNoQuota = angular.element(markupNoQuota); $compile($elementNoQuota)($scope); $scope.$apply(); })); it('Max chart should be compiled', function () { expect($elementMax.html().trim()).not.toBe(''); }); it('OverMax chart should be compiled', function () { expect($elementOverMax.html().trim()).not.toBe(''); }); it('Total chart should be compiled', function () { expect($elementTotal.html().trim()).not.toBe(''); }); it('Max chart should have svg element', function () { expect($elementMax.find('svg').length).toBe(1); }); it('OverMax chart should have svg element', function () { expect($elementOverMax.find('svg').length).toBe(1); }); it('Total chart should have svg element', function () { expect($elementTotal.find('svg').length).toBe(1); }); it('Unlimited quota chart should have hidden svg element', function () { expect($elementNoQuota.find('svg').is(':hidden')).toBe(true); }); it('Max chart should have 3 path elements', function () { expect($elementMax.find('path.slice').length).toBe(3); }); it('OverMax chart should have 3 path elements', function () { expect($elementOverMax.find('path.slice').length).toBe(3); }); it('Total chart should have 3 path elements', function () { expect($elementTotal.find('path.slice').length).toBe(3); }); it('Max chart should have correct css classes for slices', function () { var slices = $elementMax.find('path.slice'); expect(angular.element(slices[0]).attr('class')).toBe('slice usage'); expect(angular.element(slices[1]).attr('class')).toBe('slice added'); expect(angular.element(slices[2]).attr('class')).toBe('slice remaining'); }); it('OverMax chart should have correct css classes for slices', function () { var slices = $elementOverMax.find('path.slice'); expect(angular.element(slices[0]).attr('class')).toBe('slice usage'); expect(angular.element(slices[1]).attr('class')).toBe('slice added'); expect(angular.element(slices[2]).attr('class')).toBe('slice remaining'); }); it('Total chart should have correct css classes for slices', function () { var slices = $elementTotal.find('path.slice'); expect(angular.element(slices[0]).attr('class')).toBe('slice usage'); expect(angular.element(slices[1]).attr('class')).toBe('slice added'); expect(angular.element(slices[2]).attr('class')).toBe('slice remaining'); }); it('Max chart should have title "Total Instances (8 Max)"', function () { var title = $elementMax.find('.pie-chart-title').text().trim(); expect(title).toBe('Total Instances (8 Max)'); }); it('OverMax chart should have title "Total Instances (8 Max)"', function () { var title = $elementOverMax.find('.pie-chart-title').text().trim(); expect(title).toBe('Total Instances (8 Max)'); }); it('Total chart should have title "Total Instances (8 Total)"', function () { var title = $elementTotal.find('.pie-chart-title').text().trim(); expect(title).toBe('Total Instances (8 Total)'); }); it('Unlimited Quota chart should have title "Total Instances (No Limit)"', function () { var title = $elementNoQuota.find('.pie-chart-title').text().trim(); expect(title).toBe('Total Instances (No Limit)'); }); it('Max chart should have a legend', function () { expect($elementMax.find('.pie-chart-legend').length).toBe(1); }); it('OverMax chart should have a legend', function () { expect($elementOverMax.find('.pie-chart-legend').length).toBe(1); }); it('Unlimited quotachart should have a legend', function () { expect($elementNoQuota.find('.pie-chart-legend').length).toBe(1); }); it('Total chart should have a legend', function () { expect($elementTotal.find('.pie-chart-legend').length).toBe(1); }); it ('Max chart should have correct legend keys and labels', function () { var legendKeys = $elementMax.find('.pie-chart-legend .slice-legend'); var firstKeyLabel = legendKeys[0]; var secondKeyLabel = legendKeys[1]; expect(cleanSpaces(firstKeyLabel.textContent)).toBe('1 Current Usage'); expect(cleanSpaces(secondKeyLabel.textContent)).toBe('1 Added'); }); it ('OverMax chart should have correct legend keys and labels', function () { var legendKeys = $elementOverMax.find('.pie-chart-legend .slice-legend'); var firstKeyLabel = legendKeys[0]; var secondKeyLabel = legendKeys[1]; expect(cleanSpaces(firstKeyLabel.textContent)).toBe('6 Current Usage'); expect(cleanSpaces(secondKeyLabel.textContent)).toBe('3 Added'); }); it ('OverMax chart should have "danger" class', function () { var pieChart = $elementOverMax.find('svg').parent(); expect(pieChart.hasClass('danger')).toBe(true); }); it ('Total chart should have correct legend keys and labels', function () { var legendKeys = $elementTotal.find('.pie-chart-legend .slice-legend'); var firstKeyLabel = legendKeys[0]; var secondKeyLabel = legendKeys[1]; expect(cleanSpaces(firstKeyLabel.textContent)).toEqual('1 Current Usage'); expect(cleanSpaces(secondKeyLabel.textContent)).toEqual('1 Added'); }); it ('Unlimited quota chart should have correct legend keys and labels', function () { var legendKeys = $elementNoQuota.find('.pie-chart-legend .slice-legend'); var firstKeyLabel = legendKeys[0]; var secondKeyLabel = legendKeys[1]; expect(cleanSpaces(firstKeyLabel.textContent)).toEqual('1 Current Usage'); expect(cleanSpaces(secondKeyLabel.textContent)).toEqual('1 Added'); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/charts/charts.module.js0000664000567000056710000001164312701407063027372 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc overview * @name horizon.framework.widgets.charts * @description * * # horizon.framework.widgets.charts * * The `horizon.framework.widgets.charts` module provides directives for simple charts * used in Horizon, such as the pie and donut chart. Charts are * implemented using D3. * * Requires {@link http://d3js.org `D3`} to be installed. * * | Constants | * |---------------------------------------------------------------------------| * | {@link horizon.framework.widgets.charts.constant:donutChartSettings `donutChartSettings`} | * | {@link horizon.framework.widgets.charts.constant:quotaChartDefaults `quotaChartDefaults`} | * * | Directives | * |---------------------------------------------------------------------------| * | {@link horizon.framework.widgets.charts.directive:pieChart `pieChart`} | * */ angular .module('horizon.framework.widgets.charts', []) /** * @ngdoc parameters * @name horizon.framework.widgets.charts.constant:donutChartSettings * @param {number} innerRadius Pie chart inner radius in pixels, default: 24 * @param {number} outerRadius Pie chart outer radius in pixels, default: 30 * @param {object} label with properties font-size and fill (optional) * @param {string} titleClass CSS class to override title, * default: pie-chart-title-medium * alternative: pie-chart-title-large * @param {boolean} showTitle Show title, default: true * @param {boolean} showLabel Show label, default: true * @param {boolean} showLegend Show legend default: true * @param {string} tooltipIcon Tooltip key icon, default: 'fa-square' * */ .constant('horizon.framework.widgets.charts.donutChartSettings', { innerRadius: 24, outerRadius: 36, titleClass: 'pie-chart-title-medium', showTitle: true, showLabel: true, showLegend: true, tooltipIcon: 'fa-square' }) /** * @ngdoc parameters * @name horizon.framework.widgets.charts.constant:pieChartSettings * @param {number} innerRadius Pie chart inner radius in pixels, default: 0 * @param {number} outerRadius Pie chart outer radius in pixels, default: 30 * @param {object} label with properties font-size and fill (optional) * @param {string} titleClass CSS class to override title, * default: pie-chart-title-medium * alternative: pie-chart-title-large * @param {boolean} showTitle Show title, default: true * @param {boolean} showLabel Show label, default: true * @param {boolean} showLegend Show legend default: true * @param {string} tooltipIcon Tooltip key icon, default: 'fa-square' * */ .constant('horizon.framework.widgets.charts.pieChartSettings', { innerRadius: 0, outerRadius: 30, titleClass: 'pie-chart-title-medium', showTitle: true, showLabel: true, showLegend: true, tooltipIcon: 'fa-square' }) /** * @ngdoc parameters * @name horizon.framework.widgets.charts.constant:quotaChartDefaults * @param {string} usageLabel label text for Usage, default: 'Current Usage' * @param {string} usageColorClass css class for Usage , default: 'usage' * @param {string} addedLabel label text for Added, default: 'Added' * @param {string} addedColorClass CSS class for Added , default: 'added' * @param {string} remainingLabel label text for Remaining, default: 'Remaining' * @param {string} remainingColorClass CSS class for Remaining , default: 'remaining' * */ .constant('horizon.framework.widgets.charts.quotaChartDefaults', { usageLabel: gettext('Current Usage'), usageColorClass: 'usage', addedLabel: gettext('Added'), addedColorClass: 'added', remainingLabel: gettext('Remaining'), remainingColorClass: 'remaining' }) .filter('showKeyFilter', showKeyFilter); /** * @ngdoc filter * @name horizon.framework.widgets.charts.filter:showKeyFilter * @function Filter based on 'hideKey' value of each slice * @returns {function} A filtered list of keys to show in legend * */ function showKeyFilter() { return function (items) { return items.filter(function (item) { return !item.hideKey; }); }; } })(); horizon-9.0.0/horizon/static/framework/widgets/charts/chart-tooltip.spec.js0000664000567000056710000000543612701407063030347 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; describe('chartTooltip directive', function () { var $element, $scope; beforeEach(module('templates')); beforeEach(module('horizon.framework')); beforeEach(inject(function (_$compile_, _$rootScope_) { var $compile = _$compile_; $scope = _$rootScope_.$new(); $scope.tooltipData = { enabled: true, label: 'Applied', value: 'this is my amazing value', icon: 'fa-square', iconColor: '#333333', style: {left: '10px', top: '12px'} }; var markup = ''; $element = angular.element(markup); $compile($element)($scope); $scope.$apply(); })); it('compiles', function () { expect($element.html().trim()).not.toBe(''); }); it('contains the label', function () { expect($element.find('.tooltip-key').last().text()).toContain('Applied'); }); it('contains the value', function () { expect($element.text()).toContain('this is my amazing value'); }); it('enables correctly', function () { expect($element.find('div').hasClass('tooltip-enabled')).toBe(true); }); it('disables correctly', function () { $scope.tooltipData.enabled = false; $scope.$apply(); expect($element.find('div').hasClass('tooltip-enabled')).toBe(false); }); it('apply the correct style', function () { var outerDiv = $element.find('div'); expect(outerDiv[0].style.left).toBe("10px"); expect(outerDiv[0].style.top).toBe("12px"); // There shouldn't be anything other than left and top in the style. expect(outerDiv[0].style.length).toBe(2); }); it('has the correct icon', function () { var iconSpan = $element.find('span.fa'); expect(iconSpan.hasClass('fa-square')).toBe(true); }); it('has the correct icon color', function () { var iconSpan = $element.find('span.fa'); var styleColor = iconSpan[0].style.color; if (styleColor.indexOf('rgb') === 0) { expect(styleColor).toBe('rgb(51, 51, 51)'); } else { expect(styleColor).toBe('#333333'); } }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/charts/pie-chart.scss0000664000567000056710000000365212701407063027036 0ustar jenkinsjenkins00000000000000.pie-chart { display: inline-block; .svg-pie-chart { margin: $padding-small-horizontal 0; .slice { cursor: pointer; &.usage { fill: $brand-primary; } &.added { fill: lighten($brand-primary, 20%); } &.remaining { fill: $gray-lighter; } } } .pie-chart-label { font-size: $font-size-large; text-anchor: middle; text { font-size: $font-size-large; fill: $text-color; } } .pie-chart-legend { text-align: left; .slice-legend { display: table-row; & > :last-child { padding-left: $padding-xs-horizontal; } div { display: table-cell; } .slice-key { color: transparent; display: inline-block; height: 1em; position: relative; top: 0.12em; width: 0.7em; margin-right: $padding-xs-horizontal; &.usage { background-color: $brand-primary; } &.added { background-color: lighten($brand-primary, 20%); } &.remaining { background-color: $gray-lighter; } } .chartless { font-size: $font-size-large; text-align: right; padding-top: $padding-large-vertical; font-weight: bold; &.usage { color: $brand-primary; } &.added { color: lighten($brand-primary, 20%); } &.remaining { color: $gray-lighter; } } } } } // styles for donut charts over quota .pie-chart.danger { .svg-pie-chart { .slice { &.added, &.usage, &.remaining { fill: $brand-danger; } } } .pie-chart-label { text { fill: $brand-danger; } } .pie-chart-legend { .slice-legend { .slice-key { &.added { background-color: $brand-danger; } } } } } horizon-9.0.0/horizon/static/framework/widgets/charts/pie-chart.html0000664000567000056710000000245512701407063027027 0ustar jenkinsjenkins00000000000000
{$ ::chartData.title $} ({$ model.total ? model.total + ' ' : '' $}{$ model.totalLabel $})
{$ chartData.label $}
{$ slice.value $}
{$ slice.label $}
horizon-9.0.0/horizon/static/framework/widgets/charts/pie-chart.directive.js0000664000567000056710000002061212701407063030447 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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('horizon.framework.widgets.charts') .directive('pieChart', pieChart); pieChart.$inject = [ 'horizon.framework.widgets.basePath', 'horizon.framework.widgets.charts.donutChartSettings' ]; /*eslint-disable max-len */ /** * @ngdoc directive * @name horizon.framework.widgets.charts.directive:pieChart * @element * @param {object} chart-data The chart data model * @param {string} chart-settings The custom chart settings (JSON), optional * @description * The `pieChart` directive renders a pie or donut chart using D3. The title * and legend is shown by default. Each slice is represented by a label, value, * and color (hex value or CSS class). See below for the data model. * * Data Model: * ``` * var chartData = { * title: 'Total Instances', * label: '25%', * maxLimit: 10, * overMax: false, * data: [ * { label: quotaChartDefaults.usageLabel, value: 1, colorClass: quotaChartDefaults.usageColorClass}, * { label: quotaChartDefaults.addedLabel, value: 1, colorClass: quotaChartDefaults.addedColorClass }, * { label: quotaChartDefaults.remainingLabel, value: 1, colorClass: quotaChartDefaults.remainingColorClass } * ] * }; * * title - the chart title * label - the text to show in center of chart * maxLimit - the max limit for current item (optional) * - if a maxLimit is specified, (# Max) will get added to the chart title * - otherwise (# Total) will be added to the chart title * overMax - used to notify view when max is surpassed so that we can * dynamically alter UI to warn the user (optional) * data - the data used to render chart * * Donut chart settings (donutChartSettings) and pie chart settings (pieChartSettings) * are conveniently defined as angular constants in order to encourage consistency. * To leverage the constant values, you will need to specify them as dependencies * in your controller or directive. You can also create a custom styled chart * by defining a chartSettings object in your controller and passing it in as * the chart-settings attribute value. * * var chartSettings = { * innerRadius: 24, * outerRadius: 30, * titleClass: 'pie-chart-title-medium', * showTitle: true, * showLabel: true, * showLegend: true, * tooltipIcon: 'fa-square' * }; * ``` * * @restrict E * @scope true * * @example * ``` * Pie Chart using predefined constant: * * * Donut Chart using predefined constant: * * * Custom Chart using custom settings: * * ``` * */ /*eslint-enable max-len */ function pieChart(path, donutChartSettings) { var directive = { link: link, replace: true, restrict: 'E', scope: { chartData: '=', chartSettings: '=' }, templateUrl: path + 'charts/pie-chart.html' }; return directive; function link(scope, element) { function updateChartVisibility() { var showChart = scope.chartData.maxLimit !== Infinity; scope.chartData.showChart = showChart; scope.chartData.chartless = showChart ? '' : 'chartless'; return showChart; } var settings = {}; var showChart = updateChartVisibility(); // if chartSettings is defined via the attribute value, use it if (angular.isObject(scope.chartSettings)) { settings = scope.chartSettings; } else { // else default to a donut chart settings = angular.extend({}, donutChartSettings, scope.chartSettings); } settings.diameter = settings.outerRadius * 2; var model = { settings: settings, tooltipData: { enabled: false, icon: settings.tooltipIcon, style: angular.extend({}, settings.tooltip) } }; if (showChart) { var d3Elt = d3.select(element[0]); var arc = d3.svg.arc() .outerRadius(settings.outerRadius) .innerRadius(settings.innerRadius); var pie = d3.layout.pie() .sort(null) .value(function (d) { return d.value; }); } var unwatch = scope.$watch('chartData', updateChart); scope.$on('$destroy', unwatch); scope.model = model; function updateChart() { var showChart = updateChartVisibility(); angular.forEach(scope.chartData.data, function(item) { if (item.value === Infinity) { item.hideKey = true; } }); // set labels depending on whether this is a max or total chart if (!showChart) { scope.model.total = null; scope.model.totalLabel = gettext('No Limit'); } else if (angular.isDefined(scope.chartData.maxLimit)) { scope.model.total = scope.chartData.maxLimit; scope.model.totalLabel = gettext('Max'); } else { scope.model.total = d3.sum(scope.chartData.data, function (d) { return d.value; }); scope.model.totalLabel = gettext('Total'); } scope.model.tooltipData.enabled = false; // Generate or update slices if (showChart) { var chart = d3Elt.select('.slices') .selectAll('path.slice') .data(pie(scope.chartData.data)); chart.enter().append('path') .attr('class', 'slice') .attr('d', arc); // Set the color or CSS class for the fill chart.each(function (d) { var slice = d3.select(this); if (d.data.color) { slice.style('fill', d.data.color); } else if (d.data.colorClass) { slice.classed(d.data.colorClass, true); } }); chart.on('mouseenter', function (d) { showTooltip(d, this); }) .on('mouseleave', clearTooltip); // Animate the slice rendering chart.transition() .duration(500) .attrTween('d', function animate(d) { this.lastAngle = this.lastAngle || { startAngle: 0, endAngle: 0 }; var interpolate = d3.interpolate(this.lastAngle, d); this.lastAngle = interpolate(0); return function (t) { return arc(interpolate(t)); }; }); chart.exit().remove(); } } function showTooltip(d, elt) { scope.$apply(function () { var chartElt = element[0]; var eltHeight = chartElt.getBoundingClientRect().height; var titleHeight = chartElt.querySelector('div.pie-chart-title') .getBoundingClientRect() .height; var point = d3.mouse(elt); var outerRadius = scope.model.settings.outerRadius; var x = point[0] + outerRadius; var y = eltHeight - point[1] - outerRadius - titleHeight; var newTooltipData = { label: d.data.label, value: d.data.value, enabled: true, iconColor: d.data.color, iconClass: d.data.colorClass, style: { left: x + 'px', bottom: y + 'px' } }; angular.extend(scope.model.tooltipData, newTooltipData); }); } function clearTooltip() { scope.$apply(function () { scope.model.tooltipData.enabled = false; }); } } } })(); horizon-9.0.0/horizon/static/framework/widgets/charts/chart-tooltip.html0000664000567000056710000000053612701407063027742 0ustar jenkinsjenkins00000000000000
{$ tooltip.label $} {$ tooltip.value $}
horizon-9.0.0/horizon/static/framework/widgets/charts/chart-tooltip.directive.js0000664000567000056710000000340712701407063031367 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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('horizon.framework.widgets.charts') .directive('chartTooltip', chartTooltip); chartTooltip.$inject = ['horizon.framework.widgets.basePath']; /** * @ngdoc directive * @name horizon.framework.widgets.charts.directive:chartTooltip * @element * @param {object} tooltip-data The tooltip data model and styles * @description * The `chartTooltip` directive renders a tooltip showing a colored * icon, label, and value. * * Data Model and Styles: * ``` * var tooltipData = { * enabled: true, * label: 'Applied', * value: 1, * icon: 'fa-square', * iconColor: '#333333', * iconClass: 'warning', * style: { left: '10px', top: '10px' } * }; * ``` * * @restrict E * @scope tooltip: '=tooltipData' * * @example * ``` * * ``` * */ function chartTooltip(path) { var directive = { restrict: 'E', scope: { tooltip: '=tooltipData' }, templateUrl: path + 'charts/chart-tooltip.html' }; return directive; } })(); horizon-9.0.0/horizon/static/framework/widgets/help-panel/0000775000567000056710000000000012701407231025015 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/help-panel/help-panel.module.spec.js0000664000567000056710000000144312701407063031622 0ustar jenkinsjenkins00000000000000/* * 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'; describe('horizon.framework.widgets.help-panel module', function() { it('should exist', function() { expect(angular.module('horizon.framework.widgets.help-panel')).toBeDefined(); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/help-panel/help-panel.module.js0000664000567000056710000000146312701407063030673 0ustar jenkinsjenkins00000000000000/* * 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'; /** * @ngdoc overview * @name horizon.framework.widgets.help-panel * @description * * # horizon.framework.widgets.help-panel * */ angular .module('horizon.framework.widgets.help-panel', []); })(); horizon-9.0.0/horizon/static/framework/widgets/help-panel/help-panel.directive.js0000664000567000056710000000261212701407063031361 0ustar jenkinsjenkins00000000000000/* * * 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('horizon.framework.widgets.help-panel') .directive('helpPanel', helpPanel); helpPanel.$inject = [ 'horizon.framework.widgets.basePath' ]; /** * @ngdoc directive * @name horizon.framework.widgets.help-panel.directive:helpPanel * @element * @description * The `helpPanel` directive provides a help panel that can be * hidden or shown by clicking on the help/close icon. By default, * the help panel appears on the right side of the parent container. * * @restrict E * @example * ``` *
* My Content *
* ``` */ function helpPanel(path) { var directive = { templateUrl: path + 'help-panel/help-panel.html', transclude: true }; return directive; } })(); horizon-9.0.0/horizon/static/framework/widgets/help-panel/help-panel.directive.spec.js0000664000567000056710000000350112701407063032310 0ustar jenkinsjenkins00000000000000/* * * 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'; describe('help-panel directive', function () { var $compile, $scope, element; beforeEach(module('templates')); beforeEach(module('horizon.framework')); beforeEach(inject(function ($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); element = $compile('Help')($scope); $scope.$apply(); })); it('should be compiled', function () { expect(element.html().trim()).not.toBe('Help'); expect(element.text().trim()).toBe('Help'); }); it('should be closed by default', function () { expect(element[0].querySelector('#help-panel').className).toBe('collapse width'); }); it('should add "in" to class name if $scope.openHelp is true', function () { $scope.openHelp = true; $scope.$apply(); expect(element[0].querySelector('#help-panel').className).toBe('collapse width in'); }); it('should remove "in" from class name if $scope.openHelp is false', function () { $scope.openHelp = true; $scope.$apply(); $scope.openHelp = false; $scope.$apply(); expect(element[0].querySelector('#help-panel').className).toBe('collapse width'); }); }); })(); horizon-9.0.0/horizon/static/framework/widgets/help-panel/help-panel.html0000664000567000056710000000052712701407063027737 0ustar jenkinsjenkins00000000000000
horizon-9.0.0/horizon/static/framework/widgets/action-list/0000775000567000056710000000000012701407231025216 5ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/action-list/button-tooltip.row-warning.service.js0000664000567000056710000000372712701407063034503 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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('horizon.framework.widgets.action-list') .factory('horizon.framework.widgets.action-list.button-tooltip.row-warning.service', tooltip); tooltip.$inject = [ '$timeout', 'horizon.framework.widgets.basePath' ]; /** * @ngdoc factory * @name horizon.framework.widgets.action-list.button-tooltip.row-warning.service * @description * Provides the default model for buttons that can not be clicked * because there are errors with the data in the row. */ function tooltip($timeout, path) { var service = { templateUrl: path + 'action-list/warning-tooltip.html', data: { clickMessage: gettext('Click here to expand the row and view the errors.'), expandDetail: expandDetail } }; return service; /////////////// function expandDetail() { /*eslint-disable angular/controller-as-vm */ // 'this' referred here is the this for the function not the controller var row = this.element.closest('tr'); /*eslint-enable angular/controller-as-vm */ if (!row.hasClass('expanded')) { // Timeout needed to prevent // $apply already in progress error $timeout(function() { row.find('[hz-expand-detail]').click(); }, 0, false); } } } // end of tooltip })(); // end of IIFE horizon-9.0.0/horizon/static/framework/widgets/action-list/action.directive.js0000664000567000056710000000554212701407063031017 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc directive * @name horizon.framework.widgets.action-list.directive:action * @element * @description * The `action` directive represents the actions to be * displayed in a Bootstrap button group or button * dropdown. Any content within this directive element * will be appended to the button or link element. * * There are 4 button types available to an * action (`button-type` attribute): * * Default: horizon-9.0.0/horizon/static/framework/widgets/action-list/action.html0000664000567000056710000000025712701407063027370 0ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/action-list/menu-item.html0000664000567000056710000000061312701407063030007 0ustar jenkinsjenkins00000000000000
  • horizon-9.0.0/horizon/static/framework/widgets/action-list/action-list.spec.js0000664000567000056710000001315712701407063030745 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; describe('horizon.framework.widgets.action-list module', function () { it('should have been defined', function () { expect(angular.module('horizon.framework.widgets.action-list')).toBeDefined(); }); }); describe('action-list directive', function () { beforeEach(module('templates')); beforeEach(module('horizon.framework')); describe('single button dropdown', function () { var $scope, $element; beforeEach(inject(function ($injector) { var $compile = $injector.get('$compile'); var $templateCache = $injector.get('$templateCache'); var basePath = $injector.get('horizon.framework.widgets.basePath'); $scope = $injector.get('$rootScope').$new(); $scope.testList = []; $scope.item = 'test'; $scope.clickMe = function (item) { $scope.testList.push(item); }; var markup = $templateCache .get(basePath + 'action-list/action-list.single-button-dropdown.mock.html'); $element = angular.element(markup); $compile($element)($scope); $scope.$apply(); })); it('should have one dropdown button', function () { var dropdownButton = $element.find('.single-button'); expect(dropdownButton.length).toBe(1); expect(dropdownButton.text().trim()).toBe('Actions'); }); it('should have 2 menu items', function () { var menuItems = $element.find('li > a'); expect(menuItems.length).toBe(2); expect(menuItems[0].textContent.trim()).toBe('Edit'); expect(menuItems[1].textContent.trim()).toBe('Delete'); }); it('should have one item in list if link clicked', function () { $element.find('li > a').first().click(); expect($scope.testList.length).toBe(1); expect($scope.testList[0]).toBe('test'); }); }); describe('split button dropdown', function () { var $scope, $element; beforeEach(inject(function ($injector) { var $compile = $injector.get('$compile'); var $templateCache = $injector.get('$templateCache'); var basePath = $injector.get('horizon.framework.widgets.basePath'); $scope = $injector.get('$rootScope').$new(); $scope.testList = []; $scope.item = 'test'; $scope.clickMe = function (item) { $scope.testList.push(item); }; var markup = $templateCache .get(basePath + 'action-list/action-list.split-botton-dropdown.mock.html'); $element = angular.element(markup); $compile($element)($scope); $scope.$apply(); })); it('should have one dropdown button', function () { var dropdownButton = $element.find('.split-button'); expect(dropdownButton.length).toBe(1); expect(dropdownButton.text().trim()).toBe('View'); }); it('should have one caret button', function () { expect($element.find('.split-caret').length).toBe(1); expect($element.find('.fa-caret-down').length).toBe(1); }); it('should have 2 menu items', function () { var menuItems = $element.find('li > a'); expect(menuItems.length).toBe(2); expect(menuItems[0].textContent.trim()).toBe('Edit'); expect(menuItems[1].textContent.trim()).toBe('Delete'); }); it('should have one item in list if "View" clicked', function () { $element.find('.split-button').click(); expect($scope.testList.length).toBe(1); expect($scope.testList[0]).toBe('test'); }); it('should have 3 items in list if all actions clicked', function () { $element.find('.split-button').click(); $element.find('li > a').click(); expect($scope.testList.length).toBe(3); }); }); describe('button group', function () { var $scope, $element; beforeEach(inject(function ($injector) { var $compile = $injector.get('$compile'); var $templateCache = $injector.get('$templateCache'); var basePath = $injector.get('horizon.framework.widgets.basePath'); $scope = $injector.get('$rootScope').$new(); $scope.testList = []; $scope.item = 'test'; $scope.clickMe = function (item) { $scope.testList.push(item); }; var markup = $templateCache .get(basePath + 'action-list/button-group.mock.html'); $element = angular.element(markup); $compile($element)($scope); $scope.$apply(); })); it('should have 3 buttons in group', function () { var buttons = $element.find('button'); expect(buttons.length).toBe(3); expect(buttons[0].textContent.trim()).toBe('View'); expect(buttons[1].textContent.trim()).toBe('Edit'); expect(buttons[2].textContent.trim()).toBe('Delete'); }); it('should have 3 items in list if all actions clicked', function () { $element.find('button').click(); expect($scope.testList.length).toBe(3); }); }); }); })(); ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000horizon-9.0.0/horizon/static/framework/widgets/action-list/action-list.single-button-dropdown.mock.htmlhorizon-9.0.0/horizon/static/framework/widgets/action-list/action-list.single-button-dropdown.mock.h0000664000567000056710000000045412701407063035176 0ustar jenkinsjenkins00000000000000 Actions Edit Delete horizon-9.0.0/horizon/static/framework/widgets/action-list/actions-delete.template.html0000664000567000056710000000016212701407063032620 0ustar jenkinsjenkins00000000000000 $text$ horizon-9.0.0/horizon/static/framework/widgets/action-list/menu.html0000664000567000056710000000010012701407063027042 0ustar jenkinsjenkins00000000000000horizon-9.0.0/horizon/static/framework/widgets/action-list/actions.row.mock.html0000664000567000056710000000010112701407063031275 0ustar jenkinsjenkins00000000000000 horizon-9.0.0/horizon/static/framework/widgets/action-list/action-list.directive.js0000664000567000056710000000622312701407063031765 0ustar jenkinsjenkins00000000000000/* * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * * 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'; /** * @ngdoc directive * @name horizon.framework.widgets.action-list.directive:actionList * @element * @description * The `actionList` directive is the wrapper for the set of * actions to be displayed in a Bootstrap button group or * button dropdown. * * The following directives/elements can be declared within * this directive element: action, menu, and notifications. * Within the menu directive element, any number of `action` * directives elements can be declared. * * If the action list should be a button dropdown, include * `dropdown` as an attribute. Additionally, any attribute * directives can be added (i.e. warning-tooltip). * * Notifications are displayed on the bottom right of the * button group. Declare any number of icons to display * within the `notifications` element. Use `ng-show` or * `ng-hide` to dynamically show/hide the icon. Make sure * to declare first to ensure the button * dropdown border radius will display as expected. * * If a button dropdown is required, declare the dropdown * button with the `button-type` attribute set to * 'single-button' or 'split-button' for a single button * dropdown or split button dropdown, respectively. See * (http://getbootstrap.com/components/#btn-dropdowns). * The remaining actions should be declared within the * `menu` directive element with the `button-type` set to * 'menu-item'. These will be converted to links in the * dropdown menu. * * See the `action` and `menu` directives below for more * information. * * @restrict E * @example * * ``` * * * * * * Actions * * * * Edit * * * Delete * * * * ``` */ angular .module('horizon.framework.widgets.action-list') .directive('actionList', actionList); function actionList() { var directive = { link: link, restrict: 'E' }; return directive; function link(scope, element) { element.addClass('btn-group'); } } })(); horizon-9.0.0/horizon/static/framework/widgets/action-list/actions.service.js0000664000567000056710000002050512701407063030660 0ustar jenkinsjenkins00000000000000/* * 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('horizon.framework.widgets.action-list') .factory('horizon.framework.widgets.action-list.actions.service', actionsService); actionsService.$inject = [ '$compile', '$http', '$parse', '$q', '$templateCache', 'horizon.framework.widgets.basePath', 'horizon.framework.util.q.extensions' ]; function actionsService( $compile, $http, $parse, $q, $templateCache, basePath, $qExtensions ) { return function(spec) { return createService( spec.scope, spec.element, spec.ctrl, spec.listType, spec.item, spec.resultHandler); }; /////////////// function createService(scope, element, ctrl, listType, item, resultHandler) { var service = { renderActions: renderActions }; return service; function renderActions(allowedActions) { allowedActions.forEach(function(allowedAction) { allowedAction.promise = getPermissions(allowedAction); allowedAction.context = allowedAction; }); $qExtensions.allSettled(allowedActions).then(renderPermittedActions); function getPermissions(allowedAction) { if (listType === 'batch') { return allowedAction.service.allowed(); } else { var itemVal = $parse(item)(scope); return allowedAction.service.allowed(itemVal); } } } /** * Render permitted actions as per the list type */ function renderPermittedActions(permittedActions) { if (permittedActions.pass.length > 0) { var templateFetch = $q.all(permittedActions.pass.map(getTemplate)); if (listType === 'batch' || permittedActions.pass.length === 1) { element.addClass('btn-addon'); templateFetch.then(addButtons); } else { templateFetch.then(addDropdown); } } } /** * Add all the buttons as a list of buttons */ function addButtons(templates) { templates.forEach(addTemplate); } /** * Add the button template as a button */ function addTemplate(template) { element.append(renderButton(template, scope)); } /** * Add all the buttons as a dropdown button group */ function addDropdown(templates) { var splitButton = getSplitButton(templates[0]); var actionList = []; for (var iCnt = 1; iCnt < templates.length; iCnt++) { actionList.push(getMenuButton(templates[iCnt])); } var actionListElem = renderList(actionList, splitButton, scope); element.append($compile(actionListElem)(scope)); } /** * Render buttons each inside the element */ function renderButton(actionTemplate, scope) { var actionElement = angular.element(actionTemplate.template); actionElement.attr('callback', actionTemplate.callback); var actionListElem = angular.element(''); actionListElem.addClass('btn-addon'); actionListElem.append(actionElement); return $compile(actionListElem)(scope); } /** * Render buttons inside a single element * with the first being a `split-button` and the rest as * `menu-item` buttons */ function renderList(actionList, splitButton, scope) { var actionListElem = angular.element(''); actionListElem.attr('dropdown', 'true'); actionListElem.append(splitButton); actionListElem.append(getMenu(actionList, scope)); return actionListElem; } /** * Get the HTML for a `split-button` */ function getSplitButton(actionTemplate) { var actionElement = angular.element(actionTemplate.template); var type = actionTemplate.type; if (type !== 'link') { type = 'button'; } actionElement.attr('button-type', 'split-' + type); actionElement.attr('action-classes', actionElement.attr('action-classes')); actionElement.attr('callback', actionTemplate.callback); return actionElement; } /** * Get the HTML for a `menu` */ function getMenu(actionList) { var menuElem = angular.element(''); menuElem.append(actionList); return menuElem; } /** * Get the HTML for a `menu-item` button */ function getMenuButton(actionTemplate) { var actionElement = angular.element(actionTemplate.template); actionElement.attr('button-type', 'menu-item'); actionElement.attr('callback', actionTemplate.callback); return actionElement; } /** * Fetch the HTML Template for the Action */ function getTemplate(permittedAction, index, permittedActions) { var defered = $q.defer(); var action = permittedAction.context; var url = getTemplateUrl(action, permittedActions.length); $http.get(url, {cache: $templateCache}).then(onTemplateGet); return defered.promise; function onTemplateGet(response) { var callback = ctrl.generateDynamicCallback(action.service, index, resultHandler); var template = response.data .replace( '$action-classes$', getActionClasses(action, index, permittedActions.length) ) .replace('$text$', action.template.text) .replace('$item$', item); defered.resolve({ template: template, type: action.template.type || 'button', callback: callback }); } } /** * Get the ActionClasses for the Action * * This returns 'btn-danger' for the 'delete' and 'danger' * action types for row and 'btn-default' for other action * type for row. * * For batch types, the classes are determined as given in the template. * */ function getActionClasses(action, index, numPermittedActions) { var actionClassesParam = action.template.actionClasses || ""; if (listType === 'row') { if (numPermittedActions === 1 || index === 0) { var actionClasses = "btn "; if (action.template.type === "delete" || action.template.type === 'danger') { actionClasses += "btn-danger "; } else { actionClasses += "btn-default "; } return actionClasses + actionClassesParam; } else { if (action.template.type === "delete" || action.template.type === 'danger') { return 'text-danger' + actionClassesParam; } else { return actionClassesParam; } } } else { return actionClassesParam; } } /** * Gets the Template URL for the Action * The template can be * 1. Explicit URL * 2. Based of a list of known templates * 3. Based of the type of List * * Uses the `listType` which can either be `row` or `batch`. */ function getTemplateUrl(action) { if (angular.isDefined(action.template.url)) { // use the given URL return action.template.url; } else if (angular.isDefined(action.template.type)) { // determine the template by the given type return basePath + 'action-list/actions-' + action.template.type + '.template.html'; } else { // determine the template by `listType` which can be row or batch return basePath + 'action-list/actions-' + listType + '.template.html'; } } } } // end of service })(); // end of IIFE horizon-9.0.0/horizon/static/framework/widgets/action-list/actions-danger.template.html0000664000567000056710000000011612701407063032615 0ustar jenkinsjenkins00000000000000 $text$ horizon-9.0.0/horizon/static/framework/widgets/action-list/actions.directive.js0000664000567000056710000002215212701407063031176 0ustar jenkinsjenkins00000000000000/* * 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('horizon.framework.widgets.action-list') .directive('actions', actions); actions.$inject = [ '$parse', 'horizon.framework.widgets.action-list.actions.service' ]; /** * @ngdoc directive * @name horizon.framework.widgets.action-list.directive:actions * @element * @description * The `actions` directive represents the actions to be * displayed in a Bootstrap button group or button * dropdown. * * * Attributes: * * @param {string} type * Type can be only be 'row' or 'batch'. * 'batch' actions are rendered as a button group, 'row' is rendered as a button dropdown menu. * 'batch' actions are typically used for actions across multiple items while * 'row' actions are used per item. * * @param {string=} item * The item to pass to the 'service' when using 'row' type. * * @param {function} result-handler * (Optional) A function that is called with the return value from a clicked actions perform * function. Ideally the action perform function returns a promise that resolves to some data * on success, but it may return just data, or no return at all, depending on the specific action * implementation. * * @param {function} allowed * Returns an array of actions that can be performed on the item(s). * * This is an array that should contain objects with the following properties: * { * template: