diff -Nru horizon-2015.1~b2/AUTHORS horizon-2015.1~b3/AUTHORS --- horizon-2015.1~b2/AUTHORS 2015-02-05 15:53:46.000000000 +0000 +++ horizon-2015.1~b3/AUTHORS 2015-03-19 19:09:09.000000000 +0000 @@ -17,6 +17,7 @@ Alex Gaynor Alexey Izbyshev Ambroise Christea +Ami Jeain Ana Krivokapic Ana Malagon Anastasia Karpinska @@ -33,6 +34,7 @@ Anton V. Yanchenko Anya Marshall Artem Osadchiy +Artem Osadchyi Arvind Somya Ashish Chandra Assaf Muller @@ -40,11 +42,14 @@ Avishay Balderman Babitha Donepudi Bartosz Fic +Behdad Fariba Bernhard M. Wiedemann Bhavanishankar Ravindra Bradley Jones Brant Knudson Brian DeHamer +Brian Fischer +Brian Tully Brian Waldon Brianna Poulos Brooklyn Chen @@ -57,6 +62,7 @@ Chaitanya Challa Charles V Bock ChenZheng +Chris Johnson Chris St. Pierre Christian Berendt Christian Berendt @@ -85,6 +91,7 @@ Denise Hale Devin Carlen Diana Whitten +Diego Garcia del Rio Dimitri Mazmanov Dirk Mueller Dmitry Tantsur @@ -94,6 +101,7 @@ Du Yujie Duncan McGreggor Edgar Magana +Ekaterina Chernova Ekaterina Fedorova EmilienM Emma Steimann @@ -119,6 +127,7 @@ Gabriel Wainer Gary W. Smith George Peristerakis +George Peristerakis Georges Dubus Georgi Demirchev Ghe Rivero @@ -192,6 +201,7 @@ Kahou Lei Kamil Rykowski Kanagaraj Manickam +Kanchan Gupta Kannan Manickam Ke WU Ke Wu @@ -208,6 +218,7 @@ Kui Shi Kun Huang Kylin CG +LIU Yulong LIU-Yulong LIU-Yulong Ladislav Smola @@ -220,6 +231,7 @@ Liang Bo Lin Hua Cheng Liz Blanchard +Luigi Toscano Luis de Bethencourt Lukas Bezdicka Malini Bhandaru @@ -232,6 +244,7 @@ Mark McLoughlin Martin Hickey Martin Pavlasek +Masahito Muroi Masco Kaliyamoorthy Matt Borland Matt Farina @@ -281,6 +294,7 @@ Radomir Dopieralski Rafi Khardalian Randy Bertram +Ravi Repaka Ray Chen Rich Bowen Rich Hagarty @@ -288,6 +302,7 @@ Rishabh Jain Rob Rob Cresswell +Rob Cresswell Rob Raymond Robert C. Barth Robert Mizielski @@ -317,11 +332,13 @@ Sean Dague Sean Dague Sergey Lukjanov +Sergey Reshetnyak Sergio Cazzolato Shamail Tahir Shane Wang Shaoquan Chen Shuichiro MAKIGAKI +Simon Chung Solly Ross Stephane Angot Stephen Gordon @@ -330,6 +347,7 @@ SudheerKall Sushil Kumar Swaminathan Vasudevan +Szymon Wroblewski Tapan Sharma Tatiana Mazur Tatiana Ovchinnikova @@ -355,6 +373,7 @@ Travis Tripp Tres Henry Trung Trinh +Tyr Johanson Tzu-Mainn Chen Vahid Hashemian Valerii Zhelezniakov @@ -365,6 +384,7 @@ Victoria Martínez de la Cruz Vincent Untz Vishvananda Ishaya +Vishwanath Jayaraman Visnusaran Murugan Vlad Okhrimenko Wlodzimierz Borkowski @@ -381,6 +401,9 @@ Yves-Gwenael Bourhis ZHU ZHU Zhang Chun +Zhang Hua +Zhang John +Zhenguo Niu Zhenguo Niu ZhiQiang Fan Zhigang Wang @@ -414,6 +437,7 @@ jing.liuqing jmatayoshi johndavidge +keshy kstev lawrancejing lawrancejing @@ -443,6 +467,7 @@ tianliang tintmy tinytmy +utsav dusad veena whg woodm1979 diff -Nru horizon-2015.1~b2/ChangeLog horizon-2015.1~b3/ChangeLog --- horizon-2015.1~b2/ChangeLog 2015-02-05 15:53:46.000000000 +0000 +++ horizon-2015.1~b3/ChangeLog 2015-03-19 19:09:08.000000000 +0000 @@ -1,25 +1,328 @@ CHANGES ======= +2015.1.0b3 +---------- + +* Adds initial SRIOV creation/config support +* Make guided job success message translatable +* Fix small typo in error in data processing wizard +* Changing terminology for jobs and job executions in data processing +* Imported Translations from Transifex +* Add fallback on sahara endpoint lookup +* Improve styling for key pair detail table +* Fixing typo in Cinder API +* Fixing typo in Glance API +* Metadata display widget +* [Launch Instance fix] Flavor table should be sorted by RAM +* Source step - fix donut chart label color +* Add Django-1.7 support +* Imported Translations from Transifex +* [Launch Instance fix] Removing step number in Wizard widget +* Allow specifying authentication URLs from settings +* [Launch Instance Fix] Add Sorting To Table in Select Source +* [Launch Instance Fix] Removing period from selecting tips +* [Launch Instance Fix] Network Step HTML cleanup +* Metadata widget support for readonly flag +* Imported Translations from Transifex +* hypervisor list and details page reports invalid data +* Source step - fix instance count limit and donut chart +* Correct ngdoc in Neutron AngularJS service +* Launch Instance Step - Network +* Fixed transcluded content not updating in action list +* Replace usage of urllib in nova rest api +* Refactor, customization and fixes of Metadata Widget +* Show ports from shared nets in floating IP assoc +* Heat resource url mapping improvement +* made all status values translatable in port table +* Imported Translations from Transifex +* Fix transfer tables example +* Imported Translations from Transifex +* Launch Instance Step - Configuration +* Launch Instance Step - Access and Security +* Launch Instance Step - Select Flavor +* Launch Instance Step - Select Source +* Launch Instance Model +* Launch instance - common style +* Clean modal forms in Orchestration Stacks +* Add "Preview Stack" action to Stacks table +* made network status value translatable +* Imported Translations from Transifex +* Redirect initial hit to login page +* Updated transfer tables +* Fix CSS, remove bad semicolon in _variables.scss +* add the package 'which' to fedora +* Fixes alignment of pencil icon in Users Table +* Guided job execution page for Sahara +* Guided cluster creation mode for Sahara +* Imported Translations from Transifex +* Follow-up patch to fix a descrition in docs +* Imported Translations from Transifex +* Angular-based Modal Wait Spinner Widget +* Modified "remote" column in the secgroup table +* added check for delay >= timeout in LB add monitor +* Support to move the horizon relative to web root +* Imported Translations from Transifex +* 'External Gateway Information' TO 'External Gateway' +* Double check admin password when update user password +* Refactor multiple references to $(document) +* Action List with Angular directives +* Fixing permissions for data processing panels +* Remove unviewable data from dashboard images (gif/png) +* Updating sahara image registry form for dynamic adds +* Always show stack status reason in topology +* Add job create test +* Imported Translations from Transifex +* Add missing "plus" symbol to the firwall Add Policy button +* Imported Translations from Transifex +* Narrow selector to eliminate JavaScript keydown perf hit +* Floating IP table should support sorting +* Angular Form Password Validation +* Allow to hide tab title bar when only one tab is available +* Set OS_PROJECT_NAME +* Replacing data_processing with data-processing +* Imported Translations from Transifex +* Base dashboard Jasmine framework +* Updated Wizard Help Panel Design +* Fixing up job binary creation in data processing +* Angular Filter files +* Add test_sahara_create_delete_job_binary test method +* Add test_sahara_image_registry +* Imported Translations from Transifex +* admin state column added in router table +* Updated from global requirements +* Fix tutorial to correctly display custom dashboard/panel +* Move variables from accordion nav to _variables.scss +* Clean up whitespace in settings files +* REST API to support create instance angular (Neutron) +* Refactor modal forms for Volumes +* Imported Translations from Transifex +* Permit using latest python-cinderclient +* Remove extra jQuery object creation in horizon.modals.js +* Allow Jasmine failures to fail test +* Fix Jasmine unit tests failing +* Avoid failure when spinner is already gone +* Fix inappropriate error message for network table +* edit option hided when firewall in pending status +* Imported Translations from Transifex +* Keystone REST API single project GET +* context['view'] not available in Serial Console +* Rest API for Users list filter +* Fixing job binary creation from existing file +* fixed row update for loadbalancer tables +* Refactor of Metadata Widget +* Missing commas in one-element tuples +* cancel next previous buttons are aligned properly +* Imported Translations from Transifex +* Creating a new volume_type's extra_specs returns a relative path +* Imported Translations from Transifex +* REST Angular for Cinder Fix +* Imported Translations from Transifex +* Pie and donut chart directive +* Load "url" in Serial Console html +* Fix router details's name empty and change inheritance project table +* Adding policy rest endpoint for angular +* Generate a drop down field for custom heat parameters +* Replaced hard coded values with oslo_utils.units constants +* Making Resource panel visible where appropriate +* New generic _wait_until method which wraps selenium waiting +* Fix typo, replace ass with as, not ass:( +* Add help text to BatchAction +* made action value translatable in firewall rules table +* Imported Translations from Transifex +* Prevent flash of left nav panels when loading page +* Fix skip decorator in integration tests +* Add django url tag to network create template +* Nova Flavor Server Extensions Rest APIs - Angular +* Refactor REST API tests for common code patterns +* Fix Jasmine unit tests failing +* Fix typo errors in comments for api modules +* Imported Translations from Transifex +* spelling mistakes on display outputs and docsstrings +* Transfer tables +* Add orchestration service status reporting +* Adding Angular IrDragNDrop to requirements.txt +* Updated from global requirements +* Fix N1K Profile creation +* Updates tab bar styling +* Refactor modal forms for Access & Security +* Imported Translations from Transifex +* Read access to config via REST +* Go button on the Instance Log page should be enable all the time +* Skip also test fixtures when the entire class is skipped +* MapR FS datasource +* Reduce page title duplication in Data Processing +* Add string for verbose name +* Error handling patterns are consistent in volumes forms +* Change Edit volume button to save +* Make the values of Stack Resource status be translatable +* Refactor modal forms for Settings dashboard +* Add log lines change to settings page and test +* Imported Translations from Transifex +* Default sort added to hzTable +* Remove unused import +* Imported Translations from Transifex +* Update to horizon tutorial.rst +* Network Rest API for Angular Front End +* makes router id visible in update router modal +* Migrate all instances from host marked for maintenance +* improved the create router form +* Cinder REST API for angular front end +* Add service catalog get for keystone REST API +* Fixing CSS namespace collision over wizard +* Updated from global requirements +* Imported Translations from Transifex +* Make "enabled" colume values display-friendly and consistent +* Updated from global requirements +* Replace unittest2 with testtools +* Base launch instance wizard +* Wait till spinner disappears +* Refactor modal forms for Ports +* Refactor modal forms for Load Balancers +* Make Default value for Groups column translatable +* Remove Heat from example local.conf +* [Sahara] Fix Node Group Templates copy +* Incorrect error message when VM deletion failed +* Remove useless deepcopy to avoid Maximum Recursion Error +* Imported Translations from Transifex +* Serial Console +* Nova REST API for angular front end +* move Users Edit Password into separate form +* Glance Rest API for Angular Front End +* Refactor modal forms for VPN +* Reduce page title duplication in Admin networking +* [Sahara] Add status description for clusters +* Reduce page title duplication in Adv net services +* made firewall status value translatable +* Make values for IPSec&VPN status be translatable +* Imported Translations from Transifex +* Updated from global requirements +* Help text clarification for data processing data sources +* Refactor modal forms for Routers +* Reduce title duplication in project networking +* Remove test skipping since #1396954 was fixed +* Allow adding to INSTALLED_APPS from local_settings.py +* Imported Translations from Transifex +* [Data processing] Fixing data source create form to be switchable +* Refactor modal forms for Firewalls +* Fixes error message during volume snapshot creation +* Refactor modal forms for Nexus1000v +* Refactor modal forms for Subnets +* Fixing Ceilometer measurements descriptions and comment typos +* Add horizon.cookies.getRaw to access raw cookie values +* Fix inconsistent sidebar toggle icon +* Fix missing icon of `Terminate instance` +* Fixes inconsistent Admin > Instances table header +* List shared firewall policies/rules in Project panel +* Refactor modal forms for Networks +* Applies ImageFormCtrl to Admin > Images +* Revert "Fix shared firewall policy can not be displayed in horizon" +* Adding constants to tables +* Fixing Bug 1421818 - Jasmine tesing failed on FF +* Improving jshint +* Extending Horizon's plug-in architecture to Jasmine +* Remove unused import NoReverseMatch +* Adds default for resource_type_names in metadata +* Imported Translations from Transifex +* Angular Confirmation Modal +* Updated from global requirements +* remove duplicate line from users/views.py +* Make auto console type in network topology work +* Use IDs in locators instead of CSS when it is possible +* Reduce page title duplication in settings and test +* Reduce page title duplication in Images & Instance +* Reduce page title duplication in Identity views +* unite the property of usage test +* Imported Translations from Transifex +* Add missing comma in Create Network help string +* Add virtual size to image detail +* [Data Processing] Fixing job binary form label text +* Set the password_autocomplete default to "off" +* Reduce page title duplication in Nexus1000v views +* Reduce page title duplication in Stacks +* Reduce page title duplication in databases +* Reduce page title duplication in Containers +* Reduce page title duplication in A and S views +* Reduce page title duplication in Admin volumes +* Reduce page title duplication in Admin views +* Reduce page title duplication in Admin Images +* Reduce page title duplication in Admin Hypervisors +* Reduce page title duplication in Admin Flavors +* Reduce page title duplication in Admin Defaults +* Reduce page title duplication in Admin Aggregates +* Re-designed and Angularized tables +* Document and improve PageTitleMixin readability +* Add missing stubs to project stacks tests +* Add missing percent-format character +* Reduced code duplication in creating page headers +* made interface status value translatable +* Use less permissive chmod in secret key test +* Imported Translations from Transifex +* made router status value translatable +* [Data Processing] Changing node group form to be switchable +* Enabling jsi18n in Jasmine spec runner +* Fixing service name typo +* Stack resources table improvement +* Make values for Database&Backup status be translatable +* Imported Translations from Transifex +* Translatable system information panel state/status +* Fix documentation for setting password_autocomplete +* Fixes inline edit icon size/placement +* added filter for keypair and security and access tables +* Change N/A to id in the message +* Imported Translations from Transifex +* made stack status value is translatable +* change the network page's layout when launch instance +* Integration test - Create and delete user +* Add support for Trove Replication +* Imported Translations from Transifex +* Imported Translations from Transifex +* Adding policy support for ceilometer +* Data Processing: changing labels of submit buttons +* Making modify usage report params modal +* Correcting JavaScript files order +* Improves Launch Cluster model in Data Processing panel +* Adds styling of "Confirm Password" Field in user forms +* Imported Translations from Transifex +* Fix import order in utils/metering.py +* add angular-bootstrap to jasmine spec runner page + 2015.1.0b2 ---------- +* Added a migrate_settings command * Imported Translations from Transifex +* Changes Identity->Projects Forms into Horizontal Forms +* Create a reusable angular-based wizard * Move to hacking 0.10 +* [Sahara] Fixed copy nodegroup template when sec groups missing +* Make values for name&status about job_executions be translatable * Fix typo in calling _is_element_visible method * Make port status and state translatable +* Make params order fixed in stack forms +* Make the values of Cluster status be translatable +* Fixes Error message during image upload due to long name * Remove unused import urlencode * added detail page for keypair +* Remove not required parentheses for tables.Column(link=..) +* Required fields' asterisk missing in 'Launch Instance' Dialog * Make test helpers properly inheritable/extendable +* Fixes Resource id mapping with Heat Resources +* [Sahara] Support Cinder availability zones +* Add "btn-create" Class to Create Volume Button * Imported Translations from Transifex * Alter verb usage for keystone REST and add Angular service +* Clean unused code for `supports_tenants` * New admin volume panel to manage/unmanage volumes * Remove extra code in memoized method in Volumes * Remove extra indention in Firewall views.py * Add wait till element methods for selenium Integration tests * Message appear N/A in the tab compute host of hypervisors page +* Make size columns be text wrapped in admin dashboard * Make values for volume status be translatable * fix bug - failed loading external angular templates +* Fix shared firewall policy can not be displayed in horizon +* added filter for stacks table * Add missing OS::Cinder::Volume resource url map * Imported Translations from Transifex * Make values for image status be translatable @@ -31,6 +334,7 @@ * angular-based help-panel widget * Imported Translations from Transifex * Imported Translations from Transifex +* Quality of life improvements to APIVersionManager * Correct the policy check call in keystone * Alert user on JavaScript requirement if it's disabled * 403 page displays trans template tag @@ -46,16 +350,19 @@ * Display loading status when database is building * Makes create time in Job Binary Details more readable * Fixes checkbox alignment in multi-select column +* Adding filtering to data_processing tables * Add *.lock to .gitignore file * Pulled CSS logic from inline-editing * Fix editing current project * Imported Translations from Transifex +* Handle RequestURITooLong error in large instance table * Clean modal forms in Identity Users * Improving data processing create job form * Correcting heat resource page title * Adding review clean up script * Improving data processing job binaries form * Refactor metering utility and view functions +* Add general function for generating random names * Throw a warning message when failed to get pool detail * Imported Translations from Transifex * Updated from global requirements @@ -72,10 +379,12 @@ * Imported Translations from Transifex * when launch instance image size changed, device_size not change * Keystone REST API for angular front end +* Moving policy engine implementation * Removing dead css * Fix spelling and description in IPv6 subnet modes * Remove extra jQuery object creation in horizon.instances.js * added filter for networks and router +* equal(=) added in project filter option * password visibility icon not shown for first page * Fix cell(User Name) inline edit with a link in user table * Imported Translations from Transifex @@ -174,6 +483,7 @@ * Prevent user from disabling her current project * Removes reference to compute api v1.1 in openrc * Potential 500 pages on allowed methods removed +* Allow all printable ASCII characters in security group names * Fix UncompressableFileError for latest horizon code * Using get_url_current_page wrapper method * Imported Translations from Transifex @@ -663,6 +973,7 @@ * Translation feedback - Correction/update of help texts * Modified docs to list supported browsers * [Sahara] Replaced several IDs with names and names with links +* Fix auto-fill on filename input * Show floating IPs from other projects of admin instance table * Replace "tenant" with "project" in visible strings * Fix host listing in live migration @@ -917,6 +1228,7 @@ * Change note about the removal of variable SESSION_SERIALIZER * preserving dashboard order for update_dashboards call * Update Twitter Bootstrap to version 3 +* Add mismatch arguments in inherited dashboard classes * Reveal icon for password field * Imported Translations from Transifex * Add metadata option to Nova Create API Wrapper diff -Nru horizon-2015.1~b2/debian/changelog horizon-2015.1~b3/debian/changelog --- horizon-2015.1~b2/debian/changelog 2015-03-16 12:15:22.000000000 +0000 +++ horizon-2015.1~b3/debian/changelog 2015-03-30 10:12:38.000000000 +0000 @@ -1,3 +1,14 @@ +horizon (1:2015.1~b3-0ubuntu1) vivid; urgency=medium + + * New upstream milestone release: + - d/control: All version requirements with upstream, drop dependency + on lockfile. + - Refresh xstatic assets. + - d/p/*: Refresh. + * d/pydist-overrides: Add overrides for oslo packages. + + -- James Page Mon, 30 Mar 2015 11:12:29 +0100 + horizon (1:2015.1~b2-0ubuntu5) vivid; urgency=medium * d/control: Restore BD on python-django-nose, fixing FTBFS. diff -Nru horizon-2015.1~b2/debian/control horizon-2015.1~b3/debian/control --- horizon-2015.1~b2/debian/control 2015-03-16 12:11:17.000000000 +0000 +++ horizon-2015.1~b3/debian/control 2015-03-23 12:17:14.000000000 +0000 @@ -17,21 +17,20 @@ python-eventlet (>= 0.16.1), python-glanceclient (>= 1:0.15.0), python-hacking (>= 0.10.0), - python-heatclient (>= 0.2.9), + python-heatclient (>= 0.3.0), python-httplib2 (>= 0.7.5), python-iso8601 (>= 0.1.9), - python-keystoneclient (>= 1:1.0.0), + python-keystoneclient (>= 1:1.1.0), python-kombu (>= 2.5.0), - python-lockfile (>= 0.8), python-memcache, python-mock (>= 1.0), python-mox (>= 0.5.3), python-netaddr (>= 0.7.12), - python-neutronclient (>= 1:2.3.6), + python-neutronclient (>= 1:2.3.11), python-novaclient (>= 1:2.18.0), python-openstack-auth (>= 1.1.7), python-oslo-concurrency (>= 1.4.1), - python-oslo-config (>= 1:1.6.0), + python-oslo-config (>= 1:1.9.0), python-oslo-i18n (>= 1.3.0), python-oslo-serialization (>= 1.2.0), python-oslo-utils (>= 1.2.0), @@ -41,7 +40,7 @@ python-pyscss (>= 1.2.1), python-saharaclient (>= 0.7.6), python-setuptools, - python-six (>= 1.7.0), + python-six (>= 1.9.0), python-sphinx (>= 1.1.2), python-swiftclient (>= 1:2.2.0), python-testtools (>= 0.9.36), @@ -66,19 +65,18 @@ python-django-pyscss (>= 1.0.3), python-eventlet (>= 0.16.1), python-glanceclient (>= 1:0.15.0), - python-heatclient (>= 0.2.9), + python-heatclient (>= 0.3.0), python-httplib2 (>= 0.7.5), python-iso8601 (>= 0.1.9), python-keystoneclient (>= 1:1.0.0), python-kombu (>= 2.5.0), - python-lockfile (>= 0.8), python-memcache, python-netaddr (>= 0.7.12), - python-neutronclient (>= 1:2.3.6), + python-neutronclient (>= 1:2.3.11), python-novaclient (>= 1:2.18.0), python-openstack-auth (>= 1.1.7), python-oslo-concurrency (>= 1.4.1), - python-oslo-config (>= 1:1.6.0), + python-oslo-config (>= 1:1.9.0), python-oslo-i18n (>= 1.3.0), python-oslo-serialization (>= 1.2.0), python-oslo-utils (>= 1.2.0), @@ -86,7 +84,7 @@ python-pint (>= 0.5), python-pyscss (>= 1.2.1), python-saharaclient (>= 0.7.6), - python-six (>= 1.7.0), + python-six (>= 1.9.0), python-swiftclient (>= 1:2.2.0), python-troveclient (>= 1:1.0.7), python-tz (>= 2013.6), diff -Nru horizon-2015.1~b2/debian/patches/add-juju-environment-download.patch horizon-2015.1~b3/debian/patches/add-juju-environment-download.patch --- horizon-2015.1~b2/debian/patches/add-juju-environment-download.patch 2015-03-16 11:33:28.000000000 +0000 +++ horizon-2015.1~b3/debian/patches/add-juju-environment-download.patch 2015-03-23 12:21:27.000000000 +0000 @@ -29,7 +29,7 @@ + tenant-name: {{ tenant_name }} --- a/openstack_dashboard/dashboards/project/access_and_security/api_access/views.py +++ b/openstack_dashboard/dashboards/project/access_and_security/api_access/views.py -@@ -150,3 +150,37 @@ class CredentialsView(forms.ModalFormMix +@@ -151,3 +151,37 @@ class CredentialsView(forms.ModalFormMix exceptions.handle(self.request, _('Unable to get EC2 credentials')) return context diff -Nru horizon-2015.1~b2/debian/patches/embedded-xstatic.patch horizon-2015.1~b3/debian/patches/embedded-xstatic.patch --- horizon-2015.1~b2/debian/patches/embedded-xstatic.patch 2015-03-16 11:33:28.000000000 +0000 +++ horizon-2015.1~b3/debian/patches/embedded-xstatic.patch 2015-03-23 12:21:15.000000000 +0000 @@ -11,13 +11,14 @@ --- a/openstack_dashboard/static_settings.py +++ b/openstack_dashboard/static_settings.py -@@ -17,72 +17,72 @@ distributions can edit or replace this f +@@ -17,78 +17,78 @@ distributions can edit or replace this f to match their distribution's standards. """ -import xstatic.main -import xstatic.pkg.angular -import xstatic.pkg.angular_bootstrap +-import xstatic.pkg.angular_irdragndrop -import xstatic.pkg.angular_smart_table -import xstatic.pkg.bootstrap_datepicker -import xstatic.pkg.bootstrap_scss @@ -34,9 +35,11 @@ -import xstatic.pkg.qunit -import xstatic.pkg.rickshaw -import xstatic.pkg.spin +-import xstatic.pkg.termjs +import horizon.xstatic.main +import horizon.xstatic.pkg.angular +import horizon.xstatic.pkg.angular_bootstrap ++import horizon.xstatic.pkg.angular_irdragndrop +import horizon.xstatic.pkg.angular_smart_table +import horizon.xstatic.pkg.bootstrap_datepicker +import horizon.xstatic.pkg.bootstrap_scss @@ -53,6 +56,7 @@ +import horizon.xstatic.pkg.qunit +import horizon.xstatic.pkg.rickshaw +import horizon.xstatic.pkg.spin ++import horizon.xstatic.pkg.termjs STATICFILES_DIRS = [ @@ -62,7 +66,10 @@ ('horizon/lib/angular', - xstatic.main.XStatic(xstatic.pkg.angular_bootstrap).base_dir), + horizon.xstatic.main.XStatic(horizon.xstatic.pkg.angular_bootstrap).base_dir), - ('horizon/lib/smart-table', + ('horizon/lib/angular', +- xstatic.main.XStatic(xstatic.pkg.angular_irdragndrop).base_dir), ++ horizon.xstatic.main.XStatic(horizon.xstatic.pkg.angular_irdragndrop).base_dir), + ('horizon/lib/angular', - xstatic.main.XStatic(xstatic.pkg.angular_smart_table).base_dir), + horizon.xstatic.main.XStatic(horizon.xstatic.pkg.angular_smart_table).base_dir), ('horizon/lib/bootstrap_datepicker', @@ -107,6 +114,9 @@ ('horizon/lib', - xstatic.main.XStatic(xstatic.pkg.spin).base_dir), + horizon.xstatic.main.XStatic(horizon.xstatic.pkg.spin).base_dir), + ('horizon/lib', +- xstatic.main.XStatic(xstatic.pkg.termjs).base_dir), ++ horizon.xstatic.main.XStatic(horizon.xstatic.pkg.termjs).base_dir), ] diff -Nru horizon-2015.1~b2/debian/patches/fix-requirements.patch horizon-2015.1~b3/debian/patches/fix-requirements.patch --- horizon-2015.1~b2/debian/patches/fix-requirements.patch 2015-03-16 11:33:28.000000000 +0000 +++ horizon-2015.1~b3/debian/patches/fix-requirements.patch 2015-03-23 12:22:27.000000000 +0000 @@ -7,4 +7,4 @@ -selenium sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 testtools>=0.9.36,!=1.2.0 - unittest2 + # This also needs xvfb library installed on your OS diff -Nru horizon-2015.1~b2/debian/patches/ubuntu_settings.patch horizon-2015.1~b3/debian/patches/ubuntu_settings.patch --- horizon-2015.1~b2/debian/patches/ubuntu_settings.patch 2015-03-16 11:33:28.000000000 +0000 +++ horizon-2015.1~b3/debian/patches/ubuntu_settings.patch 2015-03-23 12:20:14.000000000 +0000 @@ -8,8 +8,8 @@ +DEBUG = False TEMPLATE_DEBUG = DEBUG - # Required for Django 1.5. -@@ -98,25 +98,24 @@ LOCAL_PATH = os.path.dirname(os.path.abs + +@@ -113,25 +113,24 @@ LOCAL_PATH = os.path.dirname(os.path.abs # requests routed to the same dashboard instance or you set the same SECRET_KEY # for all of them. from horizon.utils import secret_key @@ -20,35 +20,35 @@ # We recommend you use memcached for development; otherwise after every reload # of the django development server, you will have to login again. To use # memcached set CACHES to something like --# CACHES = { -+CACHES = { -+ 'default': { -+ 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', -+ 'LOCATION': '127.0.0.1:11211', -+ } -+} -+ -+#CACHES = { - # 'default': { +-#CACHES = { +-# 'default': { -# 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', -# 'LOCATION': '127.0.0.1:11211', -+# 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache' - # } - #} - --CACHES = { -- 'default': { -- 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache' -- } --} +-# } +-#} - + CACHES = { + 'default': { +- 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', ++ 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', ++ 'LOCATION': '127.0.0.1:11211', + } + } + ++#CACHES = { ++# 'default': { ++# 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', ++# } ++#} ++ # Send email to the console by default EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # Or send them to /dev/null -@@ -543,3 +542,26 @@ SECURITY_GROUP_RULES = { +@@ -577,3 +576,27 @@ SECURITY_GROUP_RULES = { + # auth_token middleware are using. Allowed values are the # algorithms supported by Python's hashlib library. - # OPENSTACK_TOKEN_HASH_ALGORITHM = 'md5' - + #OPENSTACK_TOKEN_HASH_ALGORITHM = 'md5' ++ +############################################################################### +# Ubuntu Settings +############################################################################### @@ -74,7 +74,7 @@ +COMPRESS_OFFLINE = True --- a/openstack_dashboard/settings.py +++ b/openstack_dashboard/settings.py -@@ -274,8 +274,7 @@ if not SECRET_KEY: +@@ -299,8 +299,7 @@ if not SECRET_KEY: 'local') from horizon.utils import secret_key @@ -82,5 +82,5 @@ - '.secret_key_store')) + SECRET_KEY = secret_key.generate_or_read_from_file('/var/lib/openstack-dashboard/secret_key') - from openstack_dashboard import policy - POLICY_CHECK_FUNCTION = policy.check + from openstack_dashboard import policy_backend + POLICY_CHECK_FUNCTION = policy_backend.check diff -Nru horizon-2015.1~b2/debian/pydist-overrides horizon-2015.1~b3/debian/pydist-overrides --- horizon-2015.1~b2/debian/pydist-overrides 2015-03-16 11:33:28.000000000 +0000 +++ horizon-2015.1~b3/debian/pydist-overrides 2015-03-23 12:13:46.000000000 +0000 @@ -23,3 +23,16 @@ xstatic-rickshaw xstatic-smart-table xstatic-spin +oslo.context python-oslo-context +oslo.concurrency python-oslo-concurrency +oslo.config python-oslo-config +oslo.messaging python-oslo-messaging +oslo.vmware python-oslo-vmware +oslo.utils python-oslo-utils +oslo.serialization python-oslo-serialization +oslo.db python-oslo-db +oslo.i18n python-oslo-i18n +oslo.utils python-oslo-utils +oslo.rootwrap python-oslo-rootwrap +oslo.log python-oslo-log +oslo.middleware python-oslo-middleware diff -Nru horizon-2015.1~b2/doc/source/index.rst horizon-2015.1~b3/doc/source/index.rst --- horizon-2015.1~b2/doc/source/index.rst 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/doc/source/index.rst 2015-03-19 19:07:23.000000000 +0000 @@ -77,6 +77,7 @@ topics/tables topics/policy topics/testing + topics/table_actions API Reference ------------- diff -Nru horizon-2015.1~b2/doc/source/quickstart.rst horizon-2015.1~b3/doc/source/quickstart.rst --- horizon-2015.1~b2/doc/source/quickstart.rst 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/doc/source/quickstart.rst 2015-03-19 19:07:23.000000000 +0000 @@ -17,7 +17,7 @@ On Fedora-based distributions (e.g., Fedora/RHEL/CentOS/Scientific Linux):: - > sudo yum install gcc git-core python-devel python-virtualenv openssl-devel libffi-devel + > sudo yum install gcc git-core python-devel python-virtualenv openssl-devel libffi-devel which Setup ===== @@ -51,6 +51,47 @@ ``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 @@ -243,7 +284,6 @@ # ObjectStorePanels panels = (BasePanels, NetworkPanels, ObjectStorePanels) default_panel = 'overview' - supports_tenants = True ... horizon.register(Project) diff -Nru horizon-2015.1~b2/doc/source/ref/local_conf.rst horizon-2015.1~b3/doc/source/ref/local_conf.rst --- horizon-2015.1~b2/doc/source/ref/local_conf.rst 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/doc/source/ref/local_conf.rst 2015-03-19 19:07:23.000000000 +0000 @@ -41,7 +41,7 @@ enable_service q-svc enable_service q-agt enable_service q-dhcp - enable_service q-13 + enable_service q-l3 enable_service q-meta enable_service q-metering enable_service neutron @@ -50,10 +50,7 @@ enable_service q-vpn # end group - # enable Heat (orchestration) Service - enable_service heat h-api h-api-cfn h-api-cw h-eng - - # enable Sahara (data_processing) Service + # enable Sahara (data-processing) Service enable_service sahara # enable Trove (database) Service diff -Nru horizon-2015.1~b2/doc/source/topics/deployment.rst horizon-2015.1~b3/doc/source/topics/deployment.rst --- horizon-2015.1~b2/doc/source/topics/deployment.rst 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/doc/source/topics/deployment.rst 2015-03-19 19:07:23.000000000 +0000 @@ -215,7 +215,13 @@ does no harm to have the setting in earlier versions, but it does not take effect. You can also disable `browser autocompletion`_ for the authentication form by -changing the ``password_autocomplete`` attribute to ``off`` in ``horizon/conf/default.py`` +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 diff -Nru horizon-2015.1~b2/doc/source/topics/settings.rst horizon-2015.1~b3/doc/source/topics/settings.rst --- horizon-2015.1~b2/doc/source/topics/settings.rst 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/doc/source/topics/settings.rst 2015-03-19 19:07:29.000000000 +0000 @@ -212,7 +212,7 @@ .. versionadded:: 2013.1(Grizzly) -Default: ``"on"`` +Default: ``"off"`` Controls whether browser autocompletion should be enabled on the login form. Valid values are ``"on"`` and ``"off"``. @@ -255,10 +255,19 @@ Default: ``[]`` -A list of javascript files to be included in the compressed set of files that are +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) ============================ @@ -271,6 +280,18 @@ ``openstack_dashboard/local/local_settings.py``, which should be copied from ``openstack_dashboard/local/local_settings.py.example``. +``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`` -------------------- @@ -317,11 +338,13 @@ Default: ``"AUTO"`` -This setting specifies the type of in-browser VNC console used to access the +This setting specifies the type of in-browser console used to access the VMs. -Valid values are ``"AUTO"``(default), ``"VNC"``, ``"SPICE"``, ``"RDP"`` -and ``None`` (this latest value is available in version 2014.2(Juno) to allow -deactivating the in-browser console). +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 2005.1(Kilo). ``INSTANCE_LOG_LENGTH`` @@ -368,6 +391,17 @@ 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`` ------------------------------ @@ -413,7 +447,7 @@ Default:: { - "data_processing": 1.1, + "data-processing": 1.1, "identity": 2.0, "volume": 2 } @@ -428,7 +462,7 @@ use of the decimal point, so valid options would be "2.0" or "3". For example, OPENSTACK_API_VERSIONS = { - "data_processing": 1.1, + "data-processing": 1.1, "identity": 3, "volume": 2 } @@ -644,6 +678,7 @@ 'enable_vpn': True, 'profile_support': None, 'supported_provider_types': ["*"], + 'supported_vnic_types': ["*"], 'segmentation_id_range': {} } @@ -769,6 +804,19 @@ 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']`` + ``segmentation_id_range``: .. versionadded:: 2014.2(Juno) @@ -811,6 +859,19 @@ 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. + + ``POLICY_FILES`` ---------------- @@ -866,17 +927,21 @@ unusable such as ``[!]``. -``OPENSTACK_TOKEN_HASH_ALGORITHM`` ----------------------------------- +``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:///horizon, you'd set this to ``"/horizon"``. -.. 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. Django Settings (Partial) ========================= @@ -968,6 +1033,15 @@ 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: @@ -1019,10 +1093,19 @@ .. versionadded:: 2014.2(Juno) -A list of javascript files to be included in the compressed set of files that are +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. + + ``DISABLED`` ------------ diff -Nru horizon-2015.1~b2/doc/source/topics/table_actions.rst horizon-2015.1~b3/doc/source/topics/table_actions.rst --- horizon-2015.1~b2/doc/source/topics/table_actions.rst 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/doc/source/topics/table_actions.rst 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,300 @@ +============================================ +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'^\?tab=mypanel_tabs_tab$', + views.IndexView.as_view(), name='mypanel_tabs'), + 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 diff -Nru horizon-2015.1~b2/doc/source/topics/tutorial.rst horizon-2015.1~b3/doc/source/topics/tutorial.rst --- horizon-2015.1~b2/doc/source/topics/tutorial.rst 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/doc/source/topics/tutorial.rst 2015-03-19 19:07:23.000000000 +0000 @@ -201,7 +201,7 @@ default_panel = 'mypanel' # Specify the slug of the default panel. -The completed ``dashoboard.py`` file should look like +The completed ``dashboard.py`` file should look like the following:: from django.utils.translation import ugettext_lazy as _ @@ -235,7 +235,7 @@ Defining a table ~~~~~~~~~~~~~~~~ -Horizon provides a :class:`~horizon.tables.DataTable` class which simplifies +Horizon provides a :class:`~horizon.forms.SelfHandlingForm` :class:`~horizon.tables.DataTable` class which simplifies the vast majority of displaying data to an end-user. We're just going to skim the surface here, but it has a tremendous number of capabilities. @@ -274,6 +274,74 @@ object is actually structured. In reality, accessing other attributes requires an additional step. +Adding actions to a table +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Horizon provides three types of basic action classes which can be taken +on a table's data: + +- :class:`~horizon.tables.Action` +- :class:`~horizon.tables.LinkAction` +- :class:`~horizon.tables.FilterAction` + + +There are also additional actions which are extensions of the basic Action classes: + +- :class:`~horizon.tables.BatchAction` +- :class:`~horizon.tables.DeleteAction` +- :class:`~horizon.tables.UpdateAction` +- :class:`~horizon.tables.FixedFilterAction` + + + +Now let's create and add a filter action to the table. To do so, we will need +to edit the ``tables.py`` file used above. To add a filter action which will +only show rows which contain the string entered in the filter field, we +must first define the action:: + + class MyFilterAction(tables.FilterAction): + name = "myfilter" + + +.. note:: + + The action specified above will default the ``filter_type`` to be ``"query"``. + This means that the filter will use the client side table sorter. + +Then, we add that action to the table actions for our table.:: + + class InstancesTable: + class Meta: + table_actions = (MyFilterAction,) + + +The completed ``tables.py`` file should look like the following:: + + from django.utils.translation import ugettext_lazy as _ + + from horizon import tables + + + 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,) + + Defining tabs ~~~~~~~~~~~~~ @@ -414,20 +482,12 @@ from openstack_dashboard.dashboards.mydashboard.mypanel import views -Update the existing ``url`` pattern to use ``views`` :: +Replace the existing ``url`` pattern with the following line:: - url(r'^$', views.IndexView.as_view(), name='index'), + url(r'^$', + views.IndexView.as_view(), name='index'), -Insert the following lines after the existing ``url`` pattern:: - - url(r'^\?tab=mypanel_tabs__tab$', - views.IndexView.as_view(), name='mypanel_tabs'), - - -Notice that ``mypanel_tabs`` is the ``slug`` attribute defined in the -``MypanelTabs`` class in the ``tabs.py`` file. - The completed ``urls.py`` file should look like the following:: from django.conf.urls import patterns @@ -437,9 +497,8 @@ urlpatterns = patterns('', - url(r'^$', views.IndexView.as_view(), name='index'), - url(r'^\?tab=mypanel_tabs_tab$', - views.IndexView.as_view(), name='mypanel_tabs'), + url(r'^$', + views.IndexView.as_view(), name='index'), ) @@ -449,7 +508,7 @@ Open the ``index.html`` file in the ``mydashboard/mypanel/templates/mypanel`` directory, the auto-generated code is like the following:: - {% extends 'mydashboard/base.html' %} + {% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Mypanel" %}{% endblock %} @@ -457,17 +516,17 @@ {% include "horizon/common/_page_header.html" with title=_("Mypanel") %} {% endblock page_header %} - {% block mydashboard_main %} + {% block main %} {% endblock %} -Insert the following code inside the ``mydashboard_main`` block:: +The ``main`` block must be modified to insert the following code:: -
+
{{ tab_group.render }}
-
+
If you want to change the title of the ``index.html`` file to be something else, @@ -476,7 +535,7 @@ section to be something else, you can change it. For example, change it to be ``My Panel``. The updated code could be like:: - {% extends 'mydashboard/base.html' %} + {% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "My Panel" %}{% endblock %} @@ -484,7 +543,7 @@ {% include "horizon/common/_page_header.html" with title=_("My Panel") %} {% endblock page_header %} - {% block mydashboard_main %} + {% block main %}
{{ tab_group.render }} @@ -529,7 +588,7 @@ Run and check the dashboard -============================= +=========================== Everything is in place, now run ``Horizon`` on the different port:: @@ -549,6 +608,15 @@ devstack is correctly set up. Once the creation of an instance is successful, go to ``My Dashboard`` again to check the data. + +Adding a complex action to a table +================================== + +For a more detailed look into adding a table action, one that requires forms for +gathering data, you can walk through :doc:`Adding a complex action to a table +` tutorial. + + Conclusion ========== diff -Nru horizon-2015.1~b2/horizon/base.py horizon-2015.1~b3/horizon/base.py --- horizon-2015.1~b2/horizon/base.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/base.py 2015-03-19 19:07:23.000000000 +0000 @@ -412,13 +412,6 @@ to control whether or not this dashboard should appear in automatically-generated navigation. Default: ``True``. - .. attribute:: supports_tenants - - Optional boolean that indicates whether or not this dashboard includes - support for projects/tenants. If set to ``True`` this dashboard's - navigation will include a UI element that allows the user to select - project/tenant. Default: ``False``. - .. attribute:: public Boolean value to determine whether this dashboard can be viewed @@ -432,7 +425,6 @@ panels = [] default_panel = None nav = True - supports_tenants = False public = False def __repr__(self): diff -Nru horizon-2015.1~b2/horizon/conf/default.py horizon-2015.1~b3/horizon/conf/default.py --- horizon-2015.1~b2/horizon/conf/default.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/conf/default.py 2015-03-19 19:07:23.000000000 +0000 @@ -40,7 +40,7 @@ 'password_validator': {'regex': '.*', 'help_text': _("Password is not accepted")}, - 'password_autocomplete': 'on', + 'password_autocomplete': 'off', # Enable or disable simplified floating IP address management. 'simple_ip_management': True diff -Nru horizon-2015.1~b2/horizon/exceptions.py horizon-2015.1~b3/horizon/exceptions.py --- horizon-2015.1~b2/horizon/exceptions.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/exceptions.py 2015-03-19 19:07:23.000000000 +0000 @@ -172,6 +172,11 @@ return self.msg % self.attrs +class ConfigurationError(HorizonException): + """Exception to be raised when invalid settings have been provided.""" + pass + + class NotAvailable(HorizonException): """Exception to be raised when something is not available.""" pass diff -Nru horizon-2015.1~b2/horizon/forms/views.py horizon-2015.1~b3/horizon/forms/views.py --- horizon-2015.1~b2/horizon/forms/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/forms/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -18,9 +18,9 @@ from django.conf import settings from django import http from django.utils.translation import ugettext_lazy as _ -from django.views import generic from horizon import exceptions +from horizon import views ADD_TO_FIELD_HEADER = "HTTP_X_HORIZON_ADD_TO_FIELD" @@ -77,7 +77,7 @@ return context -class ModalFormView(ModalBackdropMixin, ModalFormMixin, generic.FormView): +class ModalFormView(ModalBackdropMixin, ModalFormMixin, views.HorizonFormView): """The main view class from which all views which handle forms in Horizon should inherit. It takes care of all details with processing :class:`~horizon.forms.base.SelfHandlingForm` classes, and modal concerns diff -Nru horizon-2015.1~b2/horizon/loaders.py horizon-2015.1~b3/horizon/loaders.py --- horizon-2015.1~b2/horizon/loaders.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/loaders.py 2015-03-19 19:07:29.000000000 +0000 @@ -16,16 +16,23 @@ import os +import django from django.conf import settings from django.template.base import TemplateDoesNotExist # noqa -from django.template.loader import BaseLoader # noqa + +if django.get_version() >= '1.8': + from django.template.engine import Engine + from django.template.loaders.base import Loader as tLoaderCls +else: + from django.template.loader import BaseLoader as tLoaderCls # noqa + from django.utils._os import safe_join # noqa # Set up a cache of the panel directories to search. panel_template_dirs = {} -class TemplateLoader(BaseLoader): +class TemplateLoader(tLoaderCls): is_usable = True def get_template_sources(self, template_name): @@ -54,4 +61,8 @@ raise TemplateDoesNotExist(template_name) -_loader = TemplateLoader() +if django.get_version() >= '1.8': + e = Engine() + _loader = TemplateLoader(e) +else: + _loader = TemplateLoader() diff -Nru horizon-2015.1~b2/horizon/locale/ca/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/ca/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/ca/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/ca/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Catalan (http://www.transifex.com/projects/p/horizon/language/ca/)\n" "MIME-Version: 1.0\n" @@ -17,8 +17,152 @@ "Language: ca\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filtre" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Transmet" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Cancel·la" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Disponible" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Carregant" @@ -29,9 +173,9 @@ msgstr "Sense dades disponibles" #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Hi ha hagut un error. Si us plau prova de nou." @@ -40,16 +184,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "Hi ha hagut un problema de comunicació, si us plau prova de nou. " -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filtre" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "No es pot llegir el fitxer" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "No es pot desencriptar la contrasenya" @@ -81,16 +221,12 @@ msgid "Error: " msgstr "Error: " -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Cancel·la" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Treballant" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "Hi ha hagut un error en enviar el formulari. Si us plau prova de nou." @@ -148,7 +284,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "No hi ha articles per mostrar." @@ -171,7 +307,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -179,7 +315,7 @@ msgstr[1] "" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "No està autoritzat a realitzar aquesta operació." @@ -187,6 +323,89 @@ msgid "Passwords do not match." msgstr "Les contrasenyes no coincideixen." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -259,30 +478,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "No s'han pogut obtenir els grups de seguretat." diff -Nru horizon-2015.1~b2/horizon/locale/ca/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/ca/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/ca/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/ca/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Catalan (http://www.transifex.com/projects/p/horizon/language/ca/)\n" "MIME-Version: 1.0\n" @@ -17,7 +17,7 @@ "Language: ca\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Altre" @@ -35,12 +35,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "No autoritzat: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Sense autorització. Torneu a intentar-ho." @@ -78,7 +78,7 @@ msgstr "Transmet" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Cancel·la" @@ -86,8 +86,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -307,44 +305,7 @@ msgid "Volume Storage" msgstr "" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Desa" @@ -421,27 +382,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Sense limit" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Disponible" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "" msgstr[1] "" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -451,17 +412,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" @@ -479,26 +440,34 @@ msgstr[0] "" msgstr[1] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "No és un número de port vàlid" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "No és un nombre de protocol IP vàlida" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Esta permès dos punts en rang de ports" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "El nombre de port ha de ser un nombre enter" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Processant..." diff -Nru horizon-2015.1~b2/horizon/locale/cs/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/cs/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/cs/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/cs/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Czech (http://www.transifex.com/projects/p/horizon/language/cs/)\n" "MIME-Version: 1.0\n" @@ -18,8 +18,152 @@ "Language: cs\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filtr" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Odeslat" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Zrušit" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Dostupné" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Zpět" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Další" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Načítání" @@ -30,9 +174,9 @@ msgstr "Data nejsou dostupná." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Vyskytla se chyba. Prosím zkuste to znovu později." @@ -41,16 +185,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "Při komunikaci se serverem nastal problém, zkuste to znovu." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filtr" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Soubor nelze přečíst" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Heslo nelze dešifrovat" @@ -82,16 +222,12 @@ msgid "Error: " msgstr "Chyba:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Zrušit" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Zpracovávání" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 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." @@ -149,7 +285,7 @@ msgstr "Zobrazit podrobnosti instance" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Žádné položky k zobrazení." @@ -172,7 +308,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -181,7 +317,7 @@ msgstr[2] "Zobrazeno %s položek" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Nemáte oprávnění provést tuto operaci." @@ -189,6 +325,89 @@ msgid "Passwords do not match." msgstr "Hesla se neshodují." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Nelze získat svazky snímků." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Nelze získat obraz." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Nelze získat obrazy." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -261,30 +480,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "Nelze získat sítě." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Nelze získat pár klíčů." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "Nelze získat zóny dostupnosti." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "Nelze získat konfigurace." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Nelze získat bezpečnostní skupiny." diff -Nru horizon-2015.1~b2/horizon/locale/cs/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/cs/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/cs/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/cs/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Czech (http://www.transifex.com/projects/p/horizon/language/cs/)\n" "MIME-Version: 1.0\n" @@ -18,7 +18,7 @@ "Language: cs\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Ostatní" @@ -36,12 +36,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "%(resource)s s názvem \"%(name)s\" již existuje" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Neoprávněné: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Neoprávněný přístup. Prosím zkuste se přihlásit znovu." @@ -79,7 +79,7 @@ msgstr "Odeslat" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Zrušit" @@ -87,8 +87,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -309,44 +307,7 @@ msgid "Volume Storage" msgstr "Úložiště svazků" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\n Popisná data můžete zadat přidáním položek z levého sloupce do\n pravého. Můžete také vybrat data přidaná do slovníku glance,\n nebo můžete použít volbu \"Ostatní\" použitím klíče vašeho výběru.\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Dostupná popisná data" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Duplicitní klíče nejsou povoleny" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "Žádná dostupná popisná data" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Existující popisná data" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\n Popisná data zdroje můžete zadat přesunutím položek z levého\n sloupce do pravého. V levém sloupci jsou vypsány definice dat\n z katalogu popisných dat Glance. Použijte volbu \"Ostatní\" pro \n přidání klíče vašeho výběru.\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Uložit" @@ -425,15 +386,15 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Bez limitu" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Dostupné" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" @@ -441,12 +402,12 @@ msgstr[1] "%(size)d bajty" msgstr[2] "%(size)d bajtů" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -456,17 +417,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0 bajtů" @@ -486,26 +447,34 @@ msgstr[1] "" msgstr[2] "Prodaných štěňat" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Nikdy" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Není platné číslo portu" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Není platné číslo protokolu IP" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "V rozsahu portů povolena pouze jedna pomlčka" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Číslo portu musí být celé číslo" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Zpracovávaní..." diff -Nru horizon-2015.1~b2/horizon/locale/de/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/de/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/de/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/de/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: German (http://www.transifex.com/projects/p/horizon/language/de/)\n" "MIME-Version: 1.0\n" @@ -18,8 +18,152 @@ "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filter" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Abschicken" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Abbrechen" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Verfügbar" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Zurück" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Weiter" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Lade" @@ -30,9 +174,9 @@ msgstr "Keine Daten verfügbar." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Ein Fehler ist aufgetreten. Bitte versuchen Sie es später noch einmal." @@ -41,16 +185,12 @@ 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." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filter" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Die Datei konnte nicht gelesen werden" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Das Passwort konnte nicht entschlüsselt werden" @@ -82,16 +222,12 @@ msgid "Error: " msgstr "Fehler:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Abbrechen" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "In Arbeit" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 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." @@ -149,7 +285,7 @@ msgstr "Instanz-Details anzeigen" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Kein Eintrag zum anzeigen." @@ -172,7 +308,7 @@ msgid "Please confirm your selection. " msgstr "Bitte bestätigen Sie Ihre Auswahl." -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -180,7 +316,7 @@ msgstr[1] "Zeige %s Einträge" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Keine Berechtigung für diese Aktion." @@ -188,6 +324,89 @@ msgid "Passwords do not match." msgstr "Die Passwörter stimmen nicht überein." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "Ja" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "Nein" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Datenträger-Schattenkopien können nicht aufgerufen werden." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Abbild kann nicht abgerufen werden." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Abbilder können nicht abgerufen werden." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -260,30 +479,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "Netzwerke können nicht abgerufen werden." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Schlüsselpaare können nicht abgerufen werden." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "Verfügbarkeitszonen können nicht abgerufen werden." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "Varianten können nicht abgerufen werden." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Sicherheitsgruppen können nicht abgerufen werden." diff -Nru horizon-2015.1~b2/horizon/locale/de/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/de/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/de/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/de/LC_MESSAGES/django.po 2015-03-19 19:07:29.000000000 +0000 @@ -9,8 +9,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 12:24+0000\n" +"POT-Creation-Date: 2015-03-17 02:39-0500\n" +"PO-Revision-Date: 2015-03-17 11:54+0000\n" "Last-Translator: Robert Simai\n" "Language-Team: German (http://www.transifex.com/projects/p/horizon/language/de/)\n" "MIME-Version: 1.0\n" @@ -19,7 +19,7 @@ "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Andere" @@ -37,12 +37,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Eine %(resource)s mit dem Namen \"%(name)s\" existiert bereits." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Nicht berechtigt: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Nicht autorisiert. Bitte melden Sie sich erneut an." @@ -80,7 +80,7 @@ msgstr "Abschicken" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:232 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Abbrechen" @@ -88,8 +88,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -309,44 +307,7 @@ msgid "Volume Storage" msgstr "Datenträger-Speicher" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\nSie können Metadaten angeben in dem Sie Einträge von der linken Spalten zur rechten Spalte hinzufügen. Sie können wählen, ob Sie die Metadaten zum Glance Verzeichnis hinzufügen wollen oder Sie wählen die Option \"Other\" und benutzen eine Bezeichnung Ihrer Wahl." - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Verfügbare Metadaten" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Doppelte Schlüssel sind nicht erlaubt" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:193 -msgid "No available metadata" -msgstr "Keine Metadaten vorhanden" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Existierende Metadaten" - -#: templates/horizon/common/_modal_form_update_metadata.html:209 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\nSie können Ressource-Metadaten angeben in dem Sie Einträge von der linken Spalten zur rechten Spalte verschieben. In der linken Spalte sind Metadatenangaben aus dem Glance Metadaten Katalog. Wählen Sie die Option \"Other\" um eine Bezeichnung Ihrer Wahl zu benutzen." - -#: templates/horizon/common/_modal_form_update_metadata.html:231 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Speichern" @@ -423,27 +384,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Kein Limit" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Verfügbar" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Byte" msgstr[1] "%(size)d Bytes" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -453,17 +414,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s TB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0 Bytes" @@ -481,26 +442,34 @@ msgstr[0] "Sold Puppy" msgstr[1] "Sold Puppies" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "Fälschung" + #: utils/filters.py:49 msgid "Never" msgstr "Niemals" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Keine gültige Port-Nummer" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Keine gültige IP-Protokollnummer" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Im Port-Bereich ist nur ein Doppelpunkt erlaubt" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Port-Nummer muss ganzzahlig sein" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "Die Zeichenkette darf nur druckbare ASCII-Zeichen enthalten." + #: workflows/base.py:71 msgid "Processing..." msgstr "Verarbeite..." diff -Nru horizon-2015.1~b2/horizon/locale/en/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/en/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/en/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/en/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-05 00:13-0600\n" +"POT-Creation-Date: 2015-03-19 01:16-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,8 +17,152 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "" @@ -29,9 +173,9 @@ msgstr "" #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "" @@ -40,16 +184,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "" -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "" @@ -81,16 +221,12 @@ msgid "Error: " msgstr "" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "" @@ -148,7 +284,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "" @@ -171,7 +307,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -179,7 +315,7 @@ msgstr[1] "" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "" @@ -187,6 +323,89 @@ msgid "Passwords do not match." msgstr "" +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -259,30 +478,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "" diff -Nru horizon-2015.1~b2/horizon/locale/en/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/en/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/en/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/en/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-03 00:07-0600\n" +"POT-Creation-Date: 2015-03-17 01:36-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,7 +17,7 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "" @@ -35,12 +35,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "" @@ -78,7 +78,7 @@ msgstr "" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "" @@ -86,8 +86,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -306,46 +304,7 @@ msgid "Volume Storage" msgstr "" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are " -"metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" " -"option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "" @@ -422,27 +381,27 @@ msgid "Horizon" msgstr "" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "" msgstr[1] "" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "" @@ -452,17 +411,17 @@ msgid "%s GB" msgstr "" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" @@ -480,26 +439,34 @@ msgstr[0] "" msgstr[1] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "" diff -Nru horizon-2015.1~b2/horizon/locale/en_AU/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/en_AU/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/en_AU/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/en_AU/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: English (Australia) (http://www.transifex.com/projects/p/horizon/language/en_AU/)\n" "MIME-Version: 1.0\n" @@ -18,8 +18,152 @@ "Language: en_AU\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filter" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Submit" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Cancel" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Available" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Back" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Next" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Loading" @@ -30,9 +174,9 @@ msgstr "No data available." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "An error occurred. Please try again later." @@ -41,16 +185,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "There was a problem communicating with the server, please try again." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filter" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Could not read the file" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Could not decrypt the password" @@ -82,16 +222,12 @@ msgid "Error: " msgstr "Error: " -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Cancel" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Working" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "There was an error submitting the form. Please try again." @@ -149,7 +285,7 @@ msgstr "View Instance Details" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "No items to display." @@ -172,7 +308,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -180,7 +316,7 @@ msgstr[1] "Displaying %s items" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Not authorised to do this operation." @@ -188,6 +324,89 @@ msgid "Passwords do not match." msgstr "Passwords do not match." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "Yes" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "No" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Unable to retrieve volume snapshots." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Unable to retrieve image." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Unable to retrieve images." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -260,30 +479,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "Unable to retrieve networks." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Unable to retrieve keypairs." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "Unable to retrieve availability zones." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "Unable to retrieve flavors." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Unable to retrieve security groups." diff -Nru horizon-2015.1~b2/horizon/locale/en_AU/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/en_AU/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/en_AU/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/en_AU/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: English (Australia) (http://www.transifex.com/projects/p/horizon/language/en_AU/)\n" "MIME-Version: 1.0\n" @@ -18,7 +18,7 @@ "Language: en_AU\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Other" @@ -36,12 +36,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "A %(resource)s with the name \"%(name)s\" already exists." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Unauthorised: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Unauthorised. Please try logging in again." @@ -79,7 +79,7 @@ msgstr "Submit" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Cancel" @@ -87,8 +87,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -308,44 +306,7 @@ msgid "Volume Storage" msgstr "Volume Storage" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\n You can specify metadata by adding items from the left column to\n the right column. You may select the metadata added to glance\n dictionary or you can use the \"Other\" option using a key of\n your choice.\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Available Metadata" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Duplicate keys are not allowed" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "No available metadata" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Existing Metadata" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\n You can specify resource metadata by moving items from the left\n column to the right column. In the left columns there are metadata\n definitions from the Glance Metadata Catalog. Use the \"Other\" option\n to add metadata with the key of your choice.\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Save" @@ -422,27 +383,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "No Limit" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Available" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Byte" msgstr[1] "%(size)d Bytes" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -452,17 +413,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0 Bytes" @@ -480,26 +441,34 @@ msgstr[0] "Sold Puppy" msgstr[1] "Sold Puppies" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Never" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Not a valid port number" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Not a valid IP protocol number" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "One colon allowed in port range" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Port number must be integer" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Processing..." diff -Nru horizon-2015.1~b2/horizon/locale/en_GB/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/en_GB/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/en_GB/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/en_GB/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -4,12 +4,13 @@ # # Translators: # Andi Chandler , 2014 +# Rob Cresswell , 2015 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: English (United Kingdom) (http://www.transifex.com/projects/p/horizon/language/en_GB/)\n" "MIME-Version: 1.0\n" @@ -18,8 +19,152 @@ "Language: en_GB\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +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." + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" 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 columns there are metadata definitions from the Glance Metadata Catalog. Use the \"Other\" option to add metadata with the key of your choice." + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "Min" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "Max" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "Min length" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "Max length" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "Pattern mismatch" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "Integer required" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "Decimal required" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "Required" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "Duplicate keys are not allowed" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filter" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "Available Metadata" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "Existing Metadata" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "Custom" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "No available metadata" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "No existing metadata" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Submit" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Cancel" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "Allocated" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Available" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "Select one" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "Select an item from Available items below" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "No available items" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "Expand to see allocated items" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "Expand to see available items" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "Click to show or hide" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "Re-order items using drag and drop" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "Click to see more details" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "Found %(found)s of %(total)s" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Back" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Next" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "Finish" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Loading" @@ -30,9 +175,9 @@ msgstr "No data available." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "An error occurred. Please try again later." @@ -41,16 +186,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "There was a problem communicating with the server, please try again." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filter" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Could not read the file" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Could not decrypt the password" @@ -82,16 +223,12 @@ msgid "Error: " msgstr "Error: " -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Cancel" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Working" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "There was an error submitting the form. Please try again." @@ -149,7 +286,7 @@ msgstr "View Instance Details" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "No items to display." @@ -170,9 +307,9 @@ #: static/horizon/js/horizon.tables.js:204 msgid "Please confirm your selection. " -msgstr "" +msgstr "Please confirm your selection. " -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -180,7 +317,7 @@ msgstr[1] "Displaying %s items" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Not authorised to do this operation." @@ -188,102 +325,257 @@ msgid "Passwords do not match." msgstr "Passwords do not match." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "Connecting" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "Open" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "Closing" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "Closed" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "Status: %s" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "Yes" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "No" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "TB" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "KB" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "bytes" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "Unable to retrieve volumes." + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Unable to retrieve volume snapshots." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "Unable to retrieve user configuration." + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "Unable to retrieve admin configuration." + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Unable to retrieve image." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Unable to retrieve images." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "Unable to retrieve namespaces." + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "Unable to retrieve namespace." + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" -msgstr "" +msgstr "Unable to retrieve users" #: static/horizon/js/angular/services/hz.api.keystone.js:31 msgid "Unable to create the user." -msgstr "" +msgstr "Unable to create the user." #: static/horizon/js/angular/services/hz.api.keystone.js:38 msgid "Unable to delete the users." -msgstr "" +msgstr "Unable to delete the users." #: static/horizon/js/angular/services/hz.api.keystone.js:45 msgid "Unable to retrieve the user" -msgstr "" +msgstr "Unable to retrieve the user" #: static/horizon/js/angular/services/hz.api.keystone.js:53 msgid "Unable to edit the user." -msgstr "" +msgstr "Unable to edit the user." #: static/horizon/js/angular/services/hz.api.keystone.js:60 msgid "Unable to delete the user." -msgstr "" +msgstr "Unable to delete the user." #: static/horizon/js/angular/services/hz.api.keystone.js:68 msgid "Unable to retrieve role" -msgstr "" +msgstr "Unable to retrieve role" #: static/horizon/js/angular/services/hz.api.keystone.js:75 msgid "Unable to create the role." -msgstr "" +msgstr "Unable to create the role." #: static/horizon/js/angular/services/hz.api.keystone.js:82 msgid "Unable to delete the roles." -msgstr "" +msgstr "Unable to delete the roles." #: static/horizon/js/angular/services/hz.api.keystone.js:89 msgid "Unable to retrieve the role" -msgstr "" +msgstr "Unable to retrieve the role" #: static/horizon/js/angular/services/hz.api.keystone.js:97 msgid "Unable to edit the role." -msgstr "" +msgstr "Unable to edit the role." #: static/horizon/js/angular/services/hz.api.keystone.js:104 msgid "Unable to delete the role." -msgstr "" +msgstr "Unable to delete the role." #: static/horizon/js/angular/services/hz.api.keystone.js:112 msgid "Unable to retrieve domains" -msgstr "" +msgstr "Unable to retrieve domains" #: static/horizon/js/angular/services/hz.api.keystone.js:119 msgid "Unable to create the domain." -msgstr "" +msgstr "Unable to create the domain." #: static/horizon/js/angular/services/hz.api.keystone.js:126 msgid "Unable to delete the domains." -msgstr "" +msgstr "Unable to delete the domains." #: static/horizon/js/angular/services/hz.api.keystone.js:133 msgid "Unable to retrieve the domain" -msgstr "" +msgstr "Unable to retrieve the domain" #: static/horizon/js/angular/services/hz.api.keystone.js:141 msgid "Unable to edit the domain." -msgstr "" +msgstr "Unable to edit the domain." #: static/horizon/js/angular/services/hz.api.keystone.js:148 msgid "Unable to delete the domain." -msgstr "" +msgstr "Unable to delete the domain." -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" -msgstr "" +msgstr "Unable to retrieve projects" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." -msgstr "" +msgstr "Unable to create the project." -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." -msgstr "" +msgstr "Unable to delete the projects." -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" -msgstr "" +msgstr "Unable to retrieve the project" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." -msgstr "" +msgstr "Unable to edit the project." -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." -msgstr "" +msgstr "Unable to delete the project." -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." -msgstr "" +msgstr "Unable to grant the role." + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "Unable to fetch the service catalogue." + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "Unable to retrieve networks." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "Unable to create the network." + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "Unable to retrieve subnets." + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "Unable to create the subnet." + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "Unable to retrieve ports." + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Unable to retrieve keypairs." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "Unable to create the keypair." + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "Unable to retrieve availability zones." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "Unable to retrieve limits." + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "Unable to create the server." + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "Unable to retrieve server." + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "Unable to retrieve extensions." + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "Unable to retrieve flavours." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "Unable to retrieve flavour." + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "Unable to retrieve flavour extra specs." + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "Policy check failed." + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Unable to retrieve security groups." diff -Nru horizon-2015.1~b2/horizon/locale/en_GB/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/en_GB/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/en_GB/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/en_GB/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -4,12 +4,13 @@ # # Translators: # Andi Chandler , 2014 +# Rob Cresswell , 2015 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: English (United Kingdom) (http://www.transifex.com/projects/p/horizon/language/en_GB/)\n" "MIME-Version: 1.0\n" @@ -18,7 +19,7 @@ "Language: en_GB\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Other" @@ -36,12 +37,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "A %(resource)s with the name \"%(name)s\" already exists." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Unauthorised: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Unauthorised. Please try logging in again." @@ -79,7 +80,7 @@ msgstr "Submit" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Cancel" @@ -87,8 +88,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -96,7 +95,7 @@ #: tables/actions.py:645 msgid "This action cannot be undone." -msgstr "" +msgstr "This action cannot be undone." #: tables/actions.py:767 #, python-format @@ -294,7 +293,7 @@ #: templates/horizon/common/_limit_summary.html:29 #, python-format msgid "Allocated %(used)s of %(available)s " -msgstr "" +msgstr "Allocated %(used)s of %(available)s " #: templates/horizon/common/_limit_summary.html:35 msgid "Security Groups" @@ -308,44 +307,7 @@ msgid "Volume Storage" msgstr "Volume Storage" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\n You can specify metadata by adding items from the left column to\n the right column. You may select the metadata added to glance\n dictionary or you can use the \"Other\" option using a key of\n your choice.\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Available Metadata" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Duplicate keys are not allowed" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "No available metadata" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Existing Metadata" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\n You can specify resource metadata by moving items from the left\n column to the right column. In the left columns there are metadata\n definitions from the Glance Metadata Catalog. Use the \"Other\" option\n to add metadata with the key of your choice.\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Save" @@ -408,7 +370,7 @@ #: templates/horizon/common/_usage_summary.html:24 msgid "This Period's RAM-Hours:" -msgstr "" +msgstr "This Period's RAM-Hours:" #: templates/horizon/common/_workflow.html:40 msgid "Back" @@ -422,27 +384,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "No Limit" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Available" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Byte" msgstr[1] "%(size)d Bytes" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -452,17 +414,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0 Bytes" @@ -480,26 +442,34 @@ msgstr[0] "" msgstr[1] "Sold Puppies" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "Fake" + #: utils/filters.py:49 msgid "Never" msgstr "Never" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Not a valid port number" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Not a valid IP protocol number" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "One colon allowed in port range" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Port number must be integer" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "The string may only contain ASCII printable characters." + #: workflows/base.py:71 msgid "Processing..." msgstr "Processing..." diff -Nru horizon-2015.1~b2/horizon/locale/es/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/es/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/es/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/es/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -3,13 +3,15 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Alberto Molina Coballes , 2015 # cametiope , 2014 +# Marian Tort , 2015 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Spanish (http://www.transifex.com/projects/p/horizon/language/es/)\n" "MIME-Version: 1.0\n" @@ -18,8 +20,152 @@ "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +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." + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "Puede especificar los metadatos de los recursos moviendo los ítems de la columna de la izquierda a la de la derecha. En las columnas de la izquierda hay definiciones de metadatos del Glance Metadata Catalog. Utilice la opcion \"Otro\" para añadir metadatos con la clave que desee. " + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "Mín." + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "Máx." + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "Longitud mín." + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "Longitud máx." + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "Discrepancia en el patrón" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "Entero obligatorio" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "Decimal obligatorio" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "Obligatorio" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "Las claves duplicadas no están permitidas" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filtrar" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "Metadatos disponibles" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "Metadatos existentes" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "Personalizar" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "Metadatos no disponibles" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "No hay metadatos existentes" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Enviar" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Cancelar " + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "Asignados" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Disponible" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "Seleccione uno" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "Seleccione un ítem de los disponibles abajo" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "No hay ítems disponibles" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "Expandir para ver los ítems asociados" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "Expandir para ver los ítems disponibles" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "Haga click para mostrar u ocultar" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "Reordene los ítems arrastrando y soltando." + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "Haga click para ver más detalles" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Anterior" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Siguiente" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "Finalizar" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Cargando" @@ -30,9 +176,9 @@ msgstr "No hay datos disponibles." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Ha ocurrido un error. Inténtelo de nuevo más tarde." @@ -41,16 +187,12 @@ 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." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filtrar" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "No se ha podido leer el fichero" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "No se ha podido descifrar la contraseña" @@ -82,16 +224,12 @@ msgid "Error: " msgstr "Error: " -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Cancelar " - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Trabajando" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "Ha ocurrido un error al enviar el formulario. Inténtelo de nuevo." @@ -149,7 +287,7 @@ msgstr "Ver detalles de la instancia" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "No hay ítems que mostrar." @@ -170,9 +308,9 @@ #: static/horizon/js/horizon.tables.js:204 msgid "Please confirm your selection. " -msgstr "" +msgstr "Confirme su selección." -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -180,7 +318,7 @@ msgstr[1] "Mostrando %s articulos" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "No tiene autorización para realizar esta operación." @@ -188,102 +326,257 @@ msgid "Passwords do not match." msgstr "Las contraseñas no coinciden." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "Conectando" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "Abierta" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "Cerrando" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "Cerrada" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "Estado: %s" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "Sí" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "No" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "No ha sido posible obtener volúmenes." + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "No ha sido posible obtener las snapshots de volúmenes." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "No ha sido posible obtener la configuración de usuario." + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "No ha sido posible obtener la configuración de admin." + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "No ha sido posible obtener la imagen." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "No ha sido posible obtener las imágenes." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "No ha sido posible obtener los espacios de nombres." + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "No ha sido posible obtener el espacio de nombres." + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" -msgstr "" +msgstr "No ha sido posible obtener los usuarios" #: static/horizon/js/angular/services/hz.api.keystone.js:31 msgid "Unable to create the user." -msgstr "" +msgstr "No ha sido posible crear el usuario." #: static/horizon/js/angular/services/hz.api.keystone.js:38 msgid "Unable to delete the users." -msgstr "" +msgstr "No ha sido posible borrar los usuarios." #: static/horizon/js/angular/services/hz.api.keystone.js:45 msgid "Unable to retrieve the user" -msgstr "" +msgstr "No ha sido posible obtener el usuario" #: static/horizon/js/angular/services/hz.api.keystone.js:53 msgid "Unable to edit the user." -msgstr "" +msgstr "No ha sido posible editar el usuario." #: static/horizon/js/angular/services/hz.api.keystone.js:60 msgid "Unable to delete the user." -msgstr "" +msgstr "No ha sido posible borrar el usuario." #: static/horizon/js/angular/services/hz.api.keystone.js:68 msgid "Unable to retrieve role" -msgstr "" +msgstr "No ha sido posible obtener el rol" #: static/horizon/js/angular/services/hz.api.keystone.js:75 msgid "Unable to create the role." -msgstr "" +msgstr "No ha sido posible crear el rol." #: static/horizon/js/angular/services/hz.api.keystone.js:82 msgid "Unable to delete the roles." -msgstr "" +msgstr "No ha sido posible borrar los roles." #: static/horizon/js/angular/services/hz.api.keystone.js:89 msgid "Unable to retrieve the role" -msgstr "" +msgstr "No ha sido posible obtener el rol" #: static/horizon/js/angular/services/hz.api.keystone.js:97 msgid "Unable to edit the role." -msgstr "" +msgstr "No ha sido posible editar el rol." #: static/horizon/js/angular/services/hz.api.keystone.js:104 msgid "Unable to delete the role." -msgstr "" +msgstr "No ha sido posible borrar el rol." #: static/horizon/js/angular/services/hz.api.keystone.js:112 msgid "Unable to retrieve domains" -msgstr "" +msgstr "No ha sido posible obtener los dominios" #: static/horizon/js/angular/services/hz.api.keystone.js:119 msgid "Unable to create the domain." -msgstr "" +msgstr "No ha sido posible crear el dominio." #: static/horizon/js/angular/services/hz.api.keystone.js:126 msgid "Unable to delete the domains." -msgstr "" +msgstr "No ha sido posible borrar los dominios." #: static/horizon/js/angular/services/hz.api.keystone.js:133 msgid "Unable to retrieve the domain" -msgstr "" +msgstr "No ha sido posible obtener el dominio" #: static/horizon/js/angular/services/hz.api.keystone.js:141 msgid "Unable to edit the domain." -msgstr "" +msgstr "No ha sido posible editar el dominio." #: static/horizon/js/angular/services/hz.api.keystone.js:148 msgid "Unable to delete the domain." -msgstr "" +msgstr "No ha sido posible borrar el dominio." -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" -msgstr "" +msgstr "No ha sido posible obtener los proyectos" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." -msgstr "" +msgstr "No ha sido posible crear el proyecto." -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." -msgstr "" +msgstr "No ha sido posible borrar los proyectos." -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" -msgstr "" +msgstr "No ha sido posible obtener el proyecto" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." -msgstr "" +msgstr "No ha sido posible editar el proyecto." -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." -msgstr "" +msgstr "No ha sido posible borrar el proyecto." -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." -msgstr "" +msgstr "No ha sido posible asignar el rol." + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "No ha sido posible obtener el catálogo de servicios." + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "No ha sido posible obtener las redes." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "No ha sido posible crear la red." + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "No ha sido posible obtener las subredes." + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "No ha sido posible crear la subred." + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "No ha sido posible obtener los puertos." + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "No ha sido posible obtener los pares de claves." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "No ha sido posible crear el par de claves." + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "No ha sido posible obtener las zonas de disponibilidad." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "No ha sido posible obtener los límites." + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "No ha sido posible crear el servidor." + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "No ha sido posible obtener el servidor." + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "No ha sido posible obtener las extensiones." + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "No ha sido posible obtener los sabores." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "No ha sido posible obtener el sabor." + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "No ha sido posible obtener las especificaciones extra del sabor." + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "Ha fallado la comprobación de política." + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "No ha sido posible obtener los grupos de seguridad." diff -Nru horizon-2015.1~b2/horizon/locale/es/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/es/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/es/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/es/LC_MESSAGES/django.po 2015-03-19 19:07:29.000000000 +0000 @@ -3,14 +3,16 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Alberto Molina Coballes , 2015 # cametiope , 2014 +# Marian Tort , 2015 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" -"Last-Translator: openstackjenkins \n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 11:32+0000\n" +"Last-Translator: Marian Tort \n" "Language-Team: Spanish (http://www.transifex.com/projects/p/horizon/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,7 +20,7 @@ "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Otro" @@ -36,12 +38,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Un %(resource)s con el nombre \"%(name)s\" ya existe." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "No autorizado: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "No autorizado. Inicie sesión de nuevo." @@ -79,7 +81,7 @@ msgstr "Enviar" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:27 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Cancelar " @@ -87,8 +89,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -96,7 +96,7 @@ #: tables/actions.py:645 msgid "This action cannot be undone." -msgstr "" +msgstr "No se puede deshacer esta acción." #: tables/actions.py:767 #, python-format @@ -294,7 +294,7 @@ #: templates/horizon/common/_limit_summary.html:29 #, python-format msgid "Allocated %(used)s of %(available)s " -msgstr "" +msgstr "Asignados %(used)s de %(available)s " #: templates/horizon/common/_limit_summary.html:35 msgid "Security Groups" @@ -308,44 +308,7 @@ msgid "Volume Storage" msgstr "Almacenamiento de volúmenes" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\nPuede especificar los metadatos añadiendo los ítems de la columna de la izquierda a la de la derecha. Puede seleccionar los metadatos añadidos al dictionario Glance o puede utilizar la opción \"Otros\" con la que clave que desee." - #: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Metadatos disponibles" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Las claves duplicadas no están permitidas" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "Metadatos no disponibles" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Metadatos existentes" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\nPuede especificar los metadatos del recurso moviendo los ítems de la columna de la izquierda a la de la derecha. La columna de la izquierda contiene definiciones de metadatos del catálogo de metadatos de Glance. Utilizar la opción \"Otros\" para añadir metadatos con la clave que desee. " - -#: templates/horizon/common/_modal_form_update_metadata.html:234 #: workflows/base.py:594 msgid "Save" msgstr "Guardar" @@ -408,7 +371,7 @@ #: templates/horizon/common/_usage_summary.html:24 msgid "This Period's RAM-Hours:" -msgstr "" +msgstr "Horas-RAM de este periodo:" #: templates/horizon/common/_workflow.html:40 msgid "Back" @@ -422,27 +385,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Sin límite" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Disponible" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d bite" msgstr[1] "%(size)d bites" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -452,19 +415,19 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" -msgstr "" +msgstr "0 Bytes" #. Translators: test code, don't really have to translate #: test/test_dashboards/dogs/puppies/tables.py:31 @@ -480,26 +443,34 @@ msgstr[0] "Mascota Vendida" msgstr[1] "Mascotas Vendidas" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Nunca" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Número de puerto no válido" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Número de protocolo IP no válido" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Un punto permitido en el rango de puerto" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "El número de puerto debe ser un entero" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "La cadena sólo puede incluir caracteres imprimibles ASCII." + #: workflows/base.py:71 msgid "Processing..." msgstr "Procesando..." diff -Nru horizon-2015.1~b2/horizon/locale/es_MX/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/es_MX/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/es_MX/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/es_MX/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Spanish (Mexico) (http://www.transifex.com/projects/p/horizon/language/es_MX/)\n" "MIME-Version: 1.0\n" @@ -17,7 +17,7 @@ "Language: es_MX\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Otro" @@ -35,12 +35,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "No autorizado: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "No autorizado. Inicie sesión de nuevo." @@ -78,7 +78,7 @@ msgstr "Enviar" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Cancelar" @@ -86,8 +86,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -307,44 +305,7 @@ msgid "Volume Storage" msgstr "" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Guardar" @@ -421,27 +382,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Sin límite" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Disponible" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "" msgstr[1] "" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -451,17 +412,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" @@ -479,26 +440,34 @@ msgstr[0] "" msgstr[1] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "No es un número de puerto valido" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "No es un número de protocolo IP válido" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr ": se permite en el rango de puertos" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "El número de puerto debe ser un número entero" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Procesando..." diff -Nru horizon-2015.1~b2/horizon/locale/fi_FI/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/fi_FI/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/fi_FI/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/fi_FI/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Finnish (Finland) (http://www.transifex.com/projects/p/horizon/language/fi_FI/)\n" "MIME-Version: 1.0\n" @@ -18,8 +18,152 @@ "Language: fi_FI\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Suodain" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Lähetä" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Keskeytä" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Saatavilla" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Takaisin" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Edellinen" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Ladataan" @@ -30,9 +174,9 @@ msgstr "Ei dataa saatavilla." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Tapahtui virhe. Yritä myöhemmin uudelleen." @@ -41,16 +185,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "Palvelinyhteydessä tapahtui virhe, yritä myöhemmin uudelleen." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Suodain" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Tiedostoa ei voitu lukea" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Salasanaa ei voitu purkaa" @@ -82,16 +222,12 @@ msgid "Error: " msgstr "Virhe:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Keskeytä" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Käsitellään" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "Lomakkeen lähetys epäonnistui. Yritä uudelleen." @@ -149,7 +285,7 @@ msgstr "Näytä instanssin tiedot" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Ei näytettävää." @@ -172,7 +308,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -180,7 +316,7 @@ msgstr[1] "Näytetään %s kohdetta" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Sinulla ei ole oikeutta tähän toimintoon." @@ -188,6 +324,89 @@ msgid "Passwords do not match." msgstr "Salasana ei täsmää" +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Ei voida hakea levyjaon varmuusvedosta." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Ei voida hakea levykuvaa." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Ei voida hakea levykuvia." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -260,30 +479,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "Ei voida hakea verkkoja. " + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Ei voida hakea avainpareja. " + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Ei voida hakea turvaryhmiä." diff -Nru horizon-2015.1~b2/horizon/locale/fi_FI/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/fi_FI/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/fi_FI/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/fi_FI/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -8,9 +8,9 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-03 20:23-0600\n" -"PO-Revision-Date: 2015-02-03 20:43+0000\n" -"Last-Translator: Toni Willberg \n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" +"Last-Translator: openstackjenkins \n" "Language-Team: Finnish (Finland) (http://www.transifex.com/projects/p/horizon/language/fi_FI/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,7 +18,7 @@ "Language: fi_FI\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Toinen" @@ -36,12 +36,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "%(resource)s nimellä \"%(name)s\" on jo olemassa." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Ei sallittu: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Ei oikeutettu: Yritä kirjautumista uudelleen." @@ -79,7 +79,7 @@ msgstr "Lähetä" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:232 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Keskeytä" @@ -87,8 +87,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -308,44 +306,7 @@ msgid "Volume Storage" msgstr "Volume Storage" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\nVoit määritellä metatiedot lisäämällä kohteita vasemmasta sarakkeesta\noikean puoleiseen sarakkeeseen. Voit valita avainsanan Glancen hakemistosta\ntai voit käyttää \"Muu\" -optiota valitaksesi oman avainsanansi." - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Käytettävissä oleva metatieto" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Avaimen pitää olla uniikki" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:193 -msgid "No available metadata" -msgstr "Metatietoa ei ole saatavilla" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Olemassa oleva metatieto" - -#: templates/horizon/common/_modal_form_update_metadata.html:209 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\nVoit määritellä resurssin metatiedot siirtämällä kohteita vasemmasta\nsarakkeesta oikean puoleiseen sarakkeeseen. Vasemmassa sarakkeessa on metatiedon\nmääritelmät Glancen metatietokatalogista. Käytä \"Muu\" optiota\nlisätäksesi metatietoa haluamallasi avainsanalla." - -#: templates/horizon/common/_modal_form_update_metadata.html:231 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Tallenna" @@ -422,27 +383,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Ei rajaa" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Saatavilla" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Tavu" msgstr[1] "%(size)d Tavua" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -452,17 +413,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0 Tavua" @@ -480,26 +441,34 @@ msgstr[0] "Myyty Koiranpentu" msgstr[1] "Myydyt Koiranpennut" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Ei koskaan" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Portin numero on virheellinen" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "IP-protokollan numero on virheellinen" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Porttialueessa sallittu vain yksi kaksoispiste" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Porttinumero pitää olla kokonaisluku" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Käsitellään..." diff -Nru horizon-2015.1~b2/horizon/locale/fil/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/fil/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/fil/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/fil/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Filipino (http://www.transifex.com/projects/p/horizon/language/fil/)\n" "MIME-Version: 1.0\n" @@ -17,7 +17,7 @@ "Language: fil\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Iba" @@ -35,12 +35,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Di-awtorisado: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Di-awtorisado. Mangyaring subukang mag-log in muli." @@ -78,7 +78,7 @@ msgstr "Submit" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Ikansela" @@ -86,8 +86,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -307,44 +305,7 @@ msgid "Volume Storage" msgstr "Volume Storage" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "I-Save" @@ -421,27 +382,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Walang Limitasyon" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Magagamit" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "" msgstr[1] "" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -451,17 +412,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" @@ -479,26 +440,34 @@ msgstr[0] "" msgstr[1] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Hindi kailanman" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Hindi wasto ang numero ng port" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Hindi wasto ang numero ng IP protocol" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Isang tutuldok o \"colon\" ang pinapayagan sa port range" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Ang Port number ay dapat isang integer" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Nagpoproseso..." diff -Nru horizon-2015.1~b2/horizon/locale/fr/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/fr/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/fr/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/fr/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -3,14 +3,15 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# alexandre ignjatovic , 2015 # Frédéric , 2014 # Maxime COQUEREL , 2015 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: French (http://www.transifex.com/projects/p/horizon/language/fr/)\n" "MIME-Version: 1.0\n" @@ -19,8 +20,152 @@ "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filtrer" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Envoyer" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Annuler" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "Alloué" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "disponible(s)" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "Sélectionner un" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "Sélectionner un élément depuis les éléments disponibles ci-dessous" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "Pas d'élément disponible" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Retour" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Suivant" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Chargement..." @@ -31,9 +176,9 @@ msgstr "Pas de données disponibles." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Une erreur s'est produite. Veuillez réessayer ultérieurement." @@ -42,16 +187,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "Problème de communication avec le serveur, veuillez réessayer." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filtrer" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Le fichier n'a pu être lu" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Le mot de passe n'a pu être déchiffré" @@ -83,16 +224,12 @@ msgid "Error: " msgstr "Erreur :" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Annuler" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Traitement en cours..." -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "Erreur lors de la soumission du formulaire. Veuillez réessayer. " @@ -150,7 +287,7 @@ msgstr "Voir les Détails de l'Instance" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Aucun élément à afficher." @@ -173,7 +310,7 @@ msgid "Please confirm your selection. " msgstr "Merci de confirmer votre sélection." -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -181,7 +318,7 @@ msgstr[1] "Affichage des items %s" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Vous n'êtes pas autorisé à effectuer cette opération." @@ -189,29 +326,112 @@ msgid "Passwords do not match." msgstr "Les mots de passe ne correspondent pas." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "Ouvert" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "Oui" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "Non" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "Go" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "Mo" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Impossible de récupérer les instantanés de volume." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Impossible de récupérer l'image." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Impossible de récupérer les images." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" -msgstr "" +msgstr "Impossible de retrouver les utilisateurs" #: static/horizon/js/angular/services/hz.api.keystone.js:31 msgid "Unable to create the user." -msgstr "" +msgstr "Impossible de créer l'utilisateur." #: static/horizon/js/angular/services/hz.api.keystone.js:38 msgid "Unable to delete the users." -msgstr "" +msgstr "Impossible de supprimer les utilisateurs." #: static/horizon/js/angular/services/hz.api.keystone.js:45 msgid "Unable to retrieve the user" -msgstr "" +msgstr "Impossible de retrouver l'utilisateur" #: static/horizon/js/angular/services/hz.api.keystone.js:53 msgid "Unable to edit the user." -msgstr "" +msgstr "Impossible d'éditer l'utilisateur" #: static/horizon/js/angular/services/hz.api.keystone.js:60 msgid "Unable to delete the user." -msgstr "" +msgstr "Impossible de supprimer l'utilisateur." #: static/horizon/js/angular/services/hz.api.keystone.js:68 msgid "Unable to retrieve role" @@ -261,30 +481,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "Impossible de récupérer les réseaux." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Impossible de récupérer les paires de clés." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "Impossible de récupérer les zones de disponibilité." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "Impossible de retrouver le serveur." + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "Impossible de récupérer les gabarits." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Impossible de récupérer les groupes de sécurité." diff -Nru horizon-2015.1~b2/horizon/locale/fr/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/fr/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/fr/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/fr/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -9,9 +9,9 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-03 03:20+0000\n" -"Last-Translator: Maxime COQUEREL \n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" +"Last-Translator: openstackjenkins \n" "Language-Team: French (http://www.transifex.com/projects/p/horizon/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,7 +19,7 @@ "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Autre" @@ -37,12 +37,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Des %(resource)s avec le nom \"%(name)s\" existent déjà." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "%s : non autorisé" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Accès non autorisé. Merci de vous reconnecter." @@ -80,7 +80,7 @@ msgstr "Envoyer" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Annuler" @@ -88,8 +88,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -309,44 +307,7 @@ msgid "Volume Storage" msgstr "Stockage de volumes" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\nVous pouvez spécifier des métadonnées en ajoutant des items depuis la colonne de gauche dans\nla colonne droite. Vous pouvez sélectionner les métadonnées ajoutées au dictionnaire\nglance ou vous pouvez utiliser une option \"Autre\" en utilisant une clé de\nvotre choix." - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Métadonnée Disponible" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Les clés dupliquées ne sont pas autorisées" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "Pas de métadonnée disponible" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Métadonnée existante" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\nVous pouvez spécifier les ressources de métadonnées en déplaçant les items de la colonne\nde gauche à la colonne de droite. Dans les colonnes de gauche il y a les définitions\ndes métadonnées depuis le Catalogue de Métadonnées Glance. Utiliser l'option \"Autre\" pour ajouter des métadonnées avec la clé de votre choix." - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Enregistrer" @@ -423,27 +384,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Pas de limite" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "disponible(s)" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Octet" msgstr[1] "%(size)d Octets" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s Ko" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s Mo" @@ -453,17 +414,17 @@ msgid "%s GB" msgstr "%s Go" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s To" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s Po" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0 Octets" @@ -481,26 +442,34 @@ msgstr[0] "Chiot vendu" msgstr[1] "Chiots Vendus" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "Faux" + #: utils/filters.py:49 msgid "Never" msgstr "Jamais" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Numéro de port invalide" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Numéro de protocole IP invalide " -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Un seul caractère deux-points autorisé dans une plage de ports" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Le numéro de port doit être un nombre entier" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Traitement en cours..." diff -Nru horizon-2015.1~b2/horizon/locale/he/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/he/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/he/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/he/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Hebrew (http://www.transifex.com/projects/p/horizon/language/he/)\n" "MIME-Version: 1.0\n" @@ -17,8 +17,152 @@ "Language: he\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "מסנן" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "שליחה" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "ביטול" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "זמין" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "הבא" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "בטעינה" @@ -29,9 +173,9 @@ msgstr "אין מידע זמין." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "אירעה שגיאה. נא לנסות שוב מאוחר יותר." @@ -40,16 +184,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "אירעה שגיאה ביצירת קשר עם השרת, נא לנסות שוב מאוחר יותר." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "מסנן" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "לא ניתן לקרוא את הקובץ" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "לא ניתן לפענח את הססמה" @@ -81,16 +221,12 @@ msgid "Error: " msgstr "שגיאה:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "ביטול" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "בפעולה" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "אירעה שגיאה בעת שליחת הטופס. נא לנסות שוב." @@ -148,7 +284,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "אין פריטים להצגה." @@ -171,7 +307,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -179,7 +315,7 @@ msgstr[1] "" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "אין לך הרשאה לביצוע פעולה זו." @@ -187,6 +323,89 @@ msgid "Passwords do not match." msgstr "הססמאות אינן תואמות." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -259,30 +478,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "" diff -Nru horizon-2015.1~b2/horizon/locale/hi/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/hi/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/hi/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/hi/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Hindi (http://www.transifex.com/projects/p/horizon/language/hi/)\n" "MIME-Version: 1.0\n" @@ -18,8 +18,152 @@ "Language: hi\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "छानना शुरू..." + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "जमा करें" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "रद्द करें" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "उपलब्ध" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "पीछे" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "अगला" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "लोड हो रहा है" @@ -30,9 +174,9 @@ msgstr "कोई आँकड़ा उपलब्ध नहीं." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "एक त्रुटि हो गई. बाद में पुन: प्रयास करें." @@ -41,16 +185,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "सर्वर के साथ संवाद स्थापित करने में कोई समस्या थी, पुन: प्रयास करें." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "छानना शुरू..." - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "फ़ाइल को पढ़ नहीं सका" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "पासवर्ड को डिक्रिप्ट नहीं कर सका" @@ -82,16 +222,12 @@ msgid "Error: " msgstr "त्रुटि: " -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "रद्द करें" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "काम चालू है" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "फार्म जमा करने में त्रुटि हुई. पुन: प्रयास करें." @@ -149,7 +285,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "दिखाने के लिए कोई सामग्रियों नहीं." @@ -172,7 +308,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -180,7 +316,7 @@ msgstr[1] " %s मद प्रदर्शित" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr " इस प्रकिया को कार्यान्वयित करने की अनुमति नहीं है" @@ -188,6 +324,89 @@ msgid "Passwords do not match." msgstr "पासवर्ड मेल नहीं खाते." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "हाँ" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "नहीं" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "मे.बा." + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "वॉल्यूम स्नैपशॉट प्राप्त करने में असमर्थ." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "बिंब को पुनः प्राप्त करने में असमर्थ." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "बिंब को पुनः प्राप्त करने में असमर्थ." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -260,30 +479,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "नेटवर्क पुनः प्राप्त करने में असमर्थ." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "उपलब्धता क्षेत्रों को पुनः प्राप्त करने में असमर्थ." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "फ्लेवर को पुनः प्राप्त करने में असमर्थ." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "सुरक्षा समूहों को पुनः प्राप्त करने में असमर्थ." diff -Nru horizon-2015.1~b2/horizon/locale/hi/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/hi/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/hi/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/hi/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -3,13 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Bineet kumar gaur , 2015 # Chandan kumar , 2014 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Hindi (http://www.transifex.com/projects/p/horizon/language/hi/)\n" "MIME-Version: 1.0\n" @@ -18,7 +19,7 @@ "Language: hi\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "अन्य" @@ -36,12 +37,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "एक %(resource)s , \"%(name)s\" से पहले से ही मौजूद है " -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "अनधिकृत: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "अनधिकृत. फिर लॉग इन करने का प्रयास करें." @@ -79,7 +80,7 @@ msgstr "जमा करें" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "रद्द करें" @@ -87,8 +88,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -96,7 +95,7 @@ #: tables/actions.py:645 msgid "This action cannot be undone." -msgstr "" +msgstr "यह क्रिया पूर्ववत नहीं किया जा सकता।" #: tables/actions.py:767 #, python-format @@ -250,7 +249,7 @@ #: templates/horizon/common/_domain_page_header.html:6 #, python-format msgid "%(context_name)s:" -msgstr "" +msgstr "%(context_name)s:" #: templates/horizon/common/_formset_table.html:35 msgid "Add a row" @@ -259,7 +258,7 @@ #: templates/horizon/common/_formset_table_row.html:15 #, python-format msgid "%(name)s: %(error)s" -msgstr "" +msgstr "%(name)s: %(error)s" #: templates/horizon/common/_limit_summary.html:4 msgid "Limit Summary" @@ -294,7 +293,7 @@ #: templates/horizon/common/_limit_summary.html:29 #, python-format msgid "Allocated %(used)s of %(available)s " -msgstr "" +msgstr " %(available)s का %(used)s आवंटित" #: templates/horizon/common/_limit_summary.html:35 msgid "Security Groups" @@ -308,44 +307,7 @@ msgid "Volume Storage" msgstr "खंड भंडारण" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\n आप दाएँ स्तंभ से बाएँ स्तंभ तक आइटम जोड़कर मेटाडाटा निर्दिष्ट कर सकते हैं.\n आप ग्लांस शब्दकोश में जोड़ा मेटाडाटा को चुन सकते हैं या आप अपनी\n पसंद का एक कुंजी का उपयोग करने के लिए \"अन्य\" विकल्प का उपयोग\n कर सकते हैं.\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "उपलब्ध मेटाडाटा" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "एक जैसे कुंजी की अनुमति नहीं है" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "मौजूदा मेटाडाटा " - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\n आप दाएँ स्तंभ से बाएँ स्तंभ तक आइटम जोड़कर स्रोत मेटाडाटा निर्दिष्ट कर सकते हैं.\n आप ग्लांस शब्दकोश में जोड़ा मेटाडाटा को चुन सकते हैं या आप अपनी\n पसंद का एक कुंजी का उपयोग करने के लिए \"अन्य\" विकल्प का उपयोग\n कर सकते हैं.\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "सहेजें" @@ -370,7 +332,7 @@ #: templates/horizon/common/_usage_summary.html:7 msgid "Select a period of time to query its usage:" -msgstr "" +msgstr "इसके उपयोग की पूछताछ करने के लिए समय की अवधि का चयन करें:" #: templates/horizon/common/_usage_summary.html:9 #, python-format @@ -392,23 +354,23 @@ #: templates/horizon/common/_usage_summary.html:20 msgid "Active Instances:" -msgstr "" +msgstr "सक्रिय इंस्टेंसेस:" #: templates/horizon/common/_usage_summary.html:21 msgid "Active RAM:" -msgstr "" +msgstr "सक्रिय रैम" #: templates/horizon/common/_usage_summary.html:22 msgid "This Period's VCPU-Hours:" -msgstr "" +msgstr "इस अवधि के VCPU-घंटे" #: templates/horizon/common/_usage_summary.html:23 msgid "This Period's GB-Hours:" -msgstr "" +msgstr "इस अवधि के गीगाबाइट-घंटे" #: templates/horizon/common/_usage_summary.html:24 msgid "This Period's RAM-Hours:" -msgstr "" +msgstr "इस अवधि के रैम-घंटे " #: templates/horizon/common/_workflow.html:40 msgid "Back" @@ -422,27 +384,27 @@ msgid "Horizon" msgstr "होरिजन" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "कोई सीमा नहीं" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "उपलब्ध" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d बाइट" msgstr[1] "%(size)d बाइट" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s केबी" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -452,19 +414,19 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" -msgstr "" +msgstr "0 बाइट" #. Translators: test code, don't really have to translate #: test/test_dashboards/dogs/puppies/tables.py:31 @@ -480,26 +442,34 @@ msgstr[0] "बेचा हुआ Puppy" msgstr[1] "बेचा हुआ Puppy" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "जाली" + #: utils/filters.py:49 msgid "Never" msgstr "कभी नहीं" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "वैध पोर्ट संख्या नहीं है" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "वैध IP प्रोटोकॉल संख्या नहीं है" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "पोर्ट श्रेणी में एक कॉलन की अनुमति " -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "पोर्ट संख्या में एक पूर्णांक होना चाहिये" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "प्रोसेसिंग..." diff -Nru horizon-2015.1~b2/horizon/locale/hu/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/hu/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/hu/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/hu/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Hungarian (http://www.transifex.com/projects/p/horizon/language/hu/)\n" "MIME-Version: 1.0\n" @@ -17,7 +17,7 @@ "Language: hu\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Egyéb" @@ -35,12 +35,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Jogosulatlan hozzáférés. Próbáljon meg belépni újra." @@ -78,7 +78,7 @@ msgstr "Beküldés" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Mégse" @@ -86,8 +86,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -307,44 +305,7 @@ msgid "Volume Storage" msgstr "" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Mentés" @@ -421,27 +382,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Nincs korlát" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Elérhető" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "" msgstr[1] "" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -451,17 +412,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" @@ -479,26 +440,34 @@ msgstr[0] "" msgstr[1] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Nem érvényes port szám" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Nem érvényes IP protokoll szám" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Port számnak egész számnak kell lennie" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Feldolgozás…" diff -Nru horizon-2015.1~b2/horizon/locale/id/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/id/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/id/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/id/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Indonesian (http://www.transifex.com/projects/p/horizon/language/id/)\n" "MIME-Version: 1.0\n" @@ -17,8 +17,152 @@ "Language: id\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Penyaring" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Kirim" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Batal" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Tersedia" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Kembali" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Berikutnya" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Menampilkan" @@ -29,9 +173,9 @@ msgstr "Data tidak tersedia." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Tejadi kesalahan. Silahkan coba kembali." @@ -40,16 +184,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "Ada masalah komunikasi dengan server, mohon di coba kembali." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Penyaring" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "File tidak terbaca" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Password tidak dapat terdekripsi " @@ -81,16 +221,12 @@ msgid "Error: " msgstr "Kesalahan:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Batal" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Bekerja" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "Terjadi kesalahan pengiriman formulir. Silahkan coba kembai." @@ -148,7 +284,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Tidak ada yang ditampilkan" @@ -171,14 +307,14 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Tidak ada wewenang untuk melakukan tindakan ini." @@ -186,6 +322,89 @@ msgid "Passwords do not match." msgstr "Password berbeda." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Tidak dapat mengambil snapshot-snapshot volume." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Tidak dapat mengambil image" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Tidak dapat mengambil image." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -258,30 +477,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "" diff -Nru horizon-2015.1~b2/horizon/locale/id/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/id/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/id/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/id/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -3,12 +3,13 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Frans Thamura , 2015 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Indonesian (http://www.transifex.com/projects/p/horizon/language/id/)\n" "MIME-Version: 1.0\n" @@ -17,13 +18,13 @@ "Language: id\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Lainnya" #: decorators.py:53 msgid "Please log in to continue." -msgstr "Masuk untuk melanjutkan" +msgstr "Harap masuk untuk melanjutkan" #: decorators.py:85 #, python-format @@ -33,14 +34,14 @@ #: exceptions.py:163 #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." -msgstr "%(resource)s dengan nama \"%(name)s\" sudah ada." +msgstr "Sebuah %(resource)s dengan nama \"%(name)s\" sudah ada." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Tidak Diijinkan: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Tidak Diijinkan. Silahkan login kembali." @@ -50,7 +51,7 @@ #: browsers/base.py:88 msgid "Navigation Item" -msgstr "Menu" +msgstr "Item Navigasi" #: browsers/views.py:41 #, python-format @@ -71,14 +72,14 @@ #: forms/fields.py:66 msgid "Invalid subnet mask" -msgstr "Subnet mask alamat IP salah." +msgstr "Subnet mask alamat IP tidak benar." #: forms/views.py:132 templates/horizon/common/_usage_summary.html:16 msgid "Submit" msgstr "Kirim" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Batal" @@ -86,8 +87,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -95,7 +94,7 @@ #: tables/actions.py:645 msgid "This action cannot be undone." -msgstr "" +msgstr "Aksi ini tidak dapat diselesaikan" #: tables/actions.py:767 #, python-format @@ -146,7 +145,7 @@ #: tables/base.py:305 msgid "-" -msgstr "" +msgstr "-" #: tables/base.py:361 #, python-format @@ -160,16 +159,16 @@ #: tables/base.py:1099 #: templates/horizon/common/_data_table_table_actions.html:47 msgid "Actions" -msgstr "Lakukan" +msgstr "Aksi" #: tables/base.py:1329 #, python-format msgid "No match returned for the id \"%s\"." -msgstr "Tidak ada hasil yang berhubungan dengan id \"%s\"." +msgstr "Tidak ada hasil yang terhubung dengan id \"%s\"." #: tables/base.py:1486 msgid "Please select a row before taking that action." -msgstr "Mohon pilih baris terlebih dahulu." +msgstr "Mohon pilih sebuah baris terlebih dahulu sebelum aksi." #: templates/_header.html:5 #, python-format @@ -197,7 +196,7 @@ msgid "" "Login as different user or go back to home " "page" -msgstr "Masuk sebagai user lain atau kembali ke halaman utama" +msgstr "Masuk sebagai user lain atau kembali ke beranda" #: templates/auth/_login.html:32 msgid "Sign In" @@ -231,24 +230,24 @@ #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" -msgstr[0] "" +msgstr[0] "Menampilkan %(counter)s item" #: templates/horizon/common/_data_table.html:77 msgid "« Prev" -msgstr "« Prev" +msgstr "« Mundur" #: templates/horizon/common/_data_table.html:80 msgid "Next »" -msgstr "Next »" +msgstr "Berikut »" #: templates/horizon/common/_data_table_table_actions.html:45 msgid "More Actions" -msgstr "Perintah Lanjutan" +msgstr "Aksi Lebih" #: templates/horizon/common/_domain_page_header.html:6 #, python-format msgid "%(context_name)s:" -msgstr "" +msgstr "%(context_name)s:" #: templates/horizon/common/_formset_table.html:35 msgid "Add a row" @@ -257,11 +256,11 @@ #: templates/horizon/common/_formset_table_row.html:15 #, python-format msgid "%(name)s: %(error)s" -msgstr "" +msgstr "%(name)s: %(error)s" #: templates/horizon/common/_limit_summary.html:4 msgid "Limit Summary" -msgstr "Ringkasan Limit" +msgstr "Ringkasan Batasan" #: templates/horizon/common/_limit_summary.html:7 msgid "Instances" @@ -292,11 +291,11 @@ #: templates/horizon/common/_limit_summary.html:29 #, python-format msgid "Allocated %(used)s of %(available)s " -msgstr "" +msgstr "Teralokasi %(used)s of %(available)s " #: templates/horizon/common/_limit_summary.html:35 msgid "Security Groups" -msgstr "Security Groups" +msgstr "Kelompok Keamanan" #: templates/horizon/common/_limit_summary.html:42 msgid "Volumes" @@ -304,46 +303,9 @@ #: templates/horizon/common/_limit_summary.html:49 msgid "Volume Storage" -msgstr "Volume Storage" - -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\nAnda dapat menentukan metadata dengan menambahkan item dari kolom sebelah kiri ke kolom sebelah kanan.\nAnda dapat memilih metadata ditambahkan ke dalam kamus glance\natau anda dapat menggunakan pilihan \"Lainnya\" menggunakan \"key\" yang anda inginkan." - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Metadata yang tersedia" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Tidak diijinkan untuk \"key\" yang sama" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Metadata yang sudah ada" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\nAnda dapat menentukan sumber metadata dengan memindahkan item dari kolom kiri ke kolom kanan.\nPada kolom kiri adalah definisi metadata dari Katalog Metadata Glance.\nGunakan pilihan \"Lainnya\" untuk menambahkan metadata sesuai dengan yang anda pilih." +msgstr "Volume Penyimpanan" -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Simpan" @@ -352,13 +314,13 @@ #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" -msgstr[0] "" +msgstr[0] "Tampilkan %(nav_items)s items" #: 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[0] "Menampilkan sebanyak %(content_items)s item" #: templates/horizon/common/_usage_summary.html:3 msgid "Usage Summary" @@ -366,14 +328,14 @@ #: templates/horizon/common/_usage_summary.html:7 msgid "Select a period of time to query its usage:" -msgstr "" +msgstr "Pilih sebuah periode waktu untuk mencari kegunaannya:" #: templates/horizon/common/_usage_summary.html:9 #, python-format msgid "" "\n" " %(start)s" -msgstr "\n %(start)s" +msgstr "\n %(start)s" #: templates/horizon/common/_usage_summary.html:13 #, python-format @@ -388,23 +350,23 @@ #: templates/horizon/common/_usage_summary.html:20 msgid "Active Instances:" -msgstr "" +msgstr "Instance aktif" #: templates/horizon/common/_usage_summary.html:21 msgid "Active RAM:" -msgstr "" +msgstr "RAM aktif:" #: templates/horizon/common/_usage_summary.html:22 msgid "This Period's VCPU-Hours:" -msgstr "" +msgstr "VCPU-Hours periode ini:" #: templates/horizon/common/_usage_summary.html:23 msgid "This Period's GB-Hours:" -msgstr "" +msgstr "GB-Hours saat ini" #: templates/horizon/common/_usage_summary.html:24 msgid "This Period's RAM-Hours:" -msgstr "" +msgstr "RAM-Hours periode ini:" #: templates/horizon/common/_workflow.html:40 msgid "Back" @@ -418,26 +380,26 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Tidak Terbatas" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Tersedia" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" -msgstr[0] "" +msgstr[0] "%(size)d Byte" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -447,52 +409,60 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" -msgstr "" +msgstr "0 Bytes" #. Translators: test code, don't really have to translate #: test/test_dashboards/dogs/puppies/tables.py:31 msgid "Sell Puppy" msgid_plural "Sell Puppies" -msgstr[0] "" +msgstr[0] "Menjual Puppies" #. Translators: test code, don't really have to translate #: test/test_dashboards/dogs/puppies/tables.py:40 msgid "Sold Puppy" msgid_plural "Sold Puppies" -msgstr[0] "" +msgstr[0] "Puppies terjual" + +#: test/tests/views.py:59 +msgid "Fake" +msgstr "Bohongan" #: utils/filters.py:49 msgid "Never" msgstr "Tidak pernah" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Nomor port yang salah" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Nomor IP yang salah" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Diijinkan sebuah titik dua dalam jangkauan port" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Nomor port harus bilangan bulat" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Sedang di proses..." diff -Nru horizon-2015.1~b2/horizon/locale/it/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/it/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/it/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/it/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Italian (http://www.transifex.com/projects/p/horizon/language/it/)\n" "MIME-Version: 1.0\n" @@ -17,7 +17,7 @@ "Language: it\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Altro" @@ -35,12 +35,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Non autorizzato: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Non autorizzato. Ritentare il login." @@ -78,7 +78,7 @@ msgstr "Invia" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Annulla" @@ -86,8 +86,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -307,44 +305,7 @@ msgid "Volume Storage" msgstr "Volume Storage" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Salva" @@ -421,27 +382,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Nessun Limite" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Disponibile" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "" msgstr[1] "" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -451,17 +412,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" @@ -479,26 +440,34 @@ msgstr[0] "" msgstr[1] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Mai" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Numero porta non valido" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Numero protocollo IP non valido" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Una virgola permessa nell'intervallo delle porte" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Il numero porta deve essere un intero" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Attendere prego..." diff -Nru horizon-2015.1~b2/horizon/locale/ja/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/ja/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/ja/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/ja/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -9,8 +9,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Japanese (http://www.transifex.com/projects/p/horizon/language/ja/)\n" "MIME-Version: 1.0\n" @@ -19,8 +19,152 @@ "Language: ja\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "フィルター" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "送信" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "取り消し" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "利用可能" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "戻る" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "次へ" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "完了" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "読み込み中" @@ -31,9 +175,9 @@ msgstr "データがありません。" #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "エラーが発生しました。後からもう一度お試しください。" @@ -42,16 +186,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "サーバーとの通信中に問題がありました。再度お試しください。" -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "フィルター" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "ファイルを読み取ることができませんでした。" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "パスワードの復号化できませんでした。" @@ -83,16 +223,12 @@ msgid "Error: " msgstr "エラー: " -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "取り消し" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "反映中" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "フォームの送信中にエラーが発生しました。再度お試しください。" @@ -150,7 +286,7 @@ msgstr "インスタンスの詳細の表示" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "表示する項目がありません" @@ -173,14 +309,14 @@ msgid "Please confirm your selection. " msgstr "選択内容を確認してください。" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "%s件表示" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "この操作を行う権限がありません。" @@ -188,102 +324,257 @@ msgid "Passwords do not match." msgstr "パスワードが一致しません。" +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "オープン" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "ステータス: %s" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "はい" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "いいえ" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "ボリューム情報を取得できません。" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "ボリュームスナップショットの一覧を取得できません。" + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "イメージ情報を取得できません。" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "イメージ一覧を取得できません。" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "名前空間を取得できません。" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "名前空間を取得できません。" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" -msgstr "" +msgstr "ユーザー一覧を取得できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:31 msgid "Unable to create the user." -msgstr "" +msgstr "ユーザーを作成できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:38 msgid "Unable to delete the users." -msgstr "" +msgstr "ユーザーを削除できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:45 msgid "Unable to retrieve the user" -msgstr "" +msgstr "ユーザーを取得できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:53 msgid "Unable to edit the user." -msgstr "" +msgstr "ユーザーを編集できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:60 msgid "Unable to delete the user." -msgstr "" +msgstr "ユーザーを削除できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:68 msgid "Unable to retrieve role" -msgstr "" +msgstr "ロールを取得できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:75 msgid "Unable to create the role." -msgstr "" +msgstr "ロールを作成できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:82 msgid "Unable to delete the roles." -msgstr "" +msgstr "ロールを削除できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:89 msgid "Unable to retrieve the role" -msgstr "" +msgstr "ロールを取得できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:97 msgid "Unable to edit the role." -msgstr "" +msgstr "ロールを編集できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:104 msgid "Unable to delete the role." -msgstr "" +msgstr "ロールを削除できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:112 msgid "Unable to retrieve domains" -msgstr "" +msgstr "ドメイン一覧を取得できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:119 msgid "Unable to create the domain." -msgstr "" +msgstr "ドメインを作成できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:126 msgid "Unable to delete the domains." -msgstr "" +msgstr "ドメインを削除できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:133 msgid "Unable to retrieve the domain" -msgstr "" +msgstr "ドメインを取得できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:141 msgid "Unable to edit the domain." -msgstr "" +msgstr "ドメインを編集できません。" #: static/horizon/js/angular/services/hz.api.keystone.js:148 msgid "Unable to delete the domain." -msgstr "" +msgstr "ドメインを削除できません。" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" -msgstr "" +msgstr "プロジェクト一覧を取得できません。" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." -msgstr "" +msgstr "プロジェクトを作成できません。" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." -msgstr "" +msgstr "プロジェクトを削除できません。" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" -msgstr "" +msgstr "プロジェクトを取得できません。" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." -msgstr "" +msgstr "プロジェクトを編集できません。" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." -msgstr "" +msgstr "プロジェクトを削除できません。" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." +msgstr "ロールを許可できません。" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "サービスカタログを取得できません。" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "ネットワーク一覧を取得できません。" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "キーペアの一覧を取得できません。" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "キーペアを取得できません。" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "アベイラビリティーゾーンを取得できません。" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "上限を取得できません。" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "サーバーを作成できません。" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "フレーバーの一覧を取得できません。" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "セキュリティーグループの一覧を取得できません。" diff -Nru horizon-2015.1~b2/horizon/locale/ja/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/ja/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/ja/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/ja/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -9,8 +9,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Japanese (http://www.transifex.com/projects/p/horizon/language/ja/)\n" "MIME-Version: 1.0\n" @@ -19,7 +19,7 @@ "Language: ja\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "その他" @@ -37,12 +37,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "名前が \"%(name)s\" の %(resource)s がすでに存在します。" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "権限がありません: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "認証されていません。もう一度ログインしてください。" @@ -80,7 +80,7 @@ msgstr "送信" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "取り消し" @@ -88,8 +88,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -308,44 +306,7 @@ msgid "Volume Storage" msgstr "ボリューム容量" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\n左の列から右の列にアイテムを移動して、メタデータを指定できます。Glance に登録されているメタデータを選択するか、\"Other\" オプションで任意のキーを指定します。\n\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "利用可能なメタデータ" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "重複するキーは使用できません" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "利用可能なメタデータはありません" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "選択済みのメタデータ" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\n左の列から右の列にアイテムを移動して、リソースのメタデータを指定できます。左の列には、Glance のメタデータカタログに登録されているメタデータの定義が表示されています。任意のキーのメタデータを追加するには、\"Other\" オプションを使ってください。\n\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "保存" @@ -406,7 +367,7 @@ #: templates/horizon/common/_usage_summary.html:24 msgid "This Period's RAM-Hours:" -msgstr "" +msgstr "指定期間中のメモリー時間" #: templates/horizon/common/_workflow.html:40 msgid "Back" @@ -420,26 +381,26 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "制限なし" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "利用可能" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)dB" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -449,17 +410,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0 バイト" @@ -475,26 +436,34 @@ msgid_plural "Sold Puppies" msgstr[0] "子犬を売りました" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "Fake" + #: utils/filters.py:49 msgid "Never" msgstr "なし" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "有効なポート番号ではありません" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "有効な IP プロトコル番号ではありません" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "ポート範囲で使用できるコロンは 1 つだけです。" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "ポート番号は整数でなければなりません" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "処理中..." diff -Nru horizon-2015.1~b2/horizon/locale/ka_GE/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/ka_GE/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/ka_GE/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/ka_GE/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Georgian (Georgia) (http://www.transifex.com/projects/p/horizon/language/ka_GE/)\n" "MIME-Version: 1.0\n" @@ -18,8 +18,152 @@ "Language: ka_GE\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "ფილტრი" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "გაუქმება" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "ხელმისაწვდომი" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "უკან" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "შემდეგი" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "იტვირთება" @@ -30,9 +174,9 @@ msgstr "მონაცებელი არაა." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "შეცდომა. გთხოვთ კიდევ სცადოთ." @@ -41,16 +185,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "სერვერთან დაკავშირებისას მოხდა შეცდომა, გთხოვთ კიდევ სცადოთ." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "ფილტრი" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "ფაილის წაკითხვა ვერ მოხერხდა" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "პაროლის დეშიფრაცია ვერ მოხერხდა" @@ -82,16 +222,12 @@ msgid "Error: " msgstr "შეცდომა:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "გაუქმება" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "ვმუშაობ" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "შეცდომა ფორმის გაგზავნისას. გთხოვთ კიდევ სცადოთ." @@ -149,7 +285,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "საჩვენებელი ელემენტები არაა" @@ -172,14 +308,14 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "%s ელემენტის ჩვენება" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "ამ ოპერაციის შესასრულებელი ავტორიზაცია არაა" @@ -187,6 +323,89 @@ msgid "Passwords do not match." msgstr "პაროლები არ ემთხვევა" +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "გბ" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "მბ" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -259,30 +478,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "" diff -Nru horizon-2015.1~b2/horizon/locale/ko_KR/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/ko_KR/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/ko_KR/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/ko_KR/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -9,8 +9,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Korean (Korea) (http://www.transifex.com/projects/p/horizon/language/ko_KR/)\n" "MIME-Version: 1.0\n" @@ -19,8 +19,152 @@ "Language: ko_KR\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "작업을 진행할 수 없습니다. 이 행의 내용에 오류가 있거나 정보가 누락되어 있습니다." + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "왼쪽열에서 오른쪽열로 항목을 이동하여 리소스 메타데이터를 지정할 수 있습니다. 왼쪽열에서 Clance 메타데이터 카탈로그에서 메타데이터를 정의할 수 있습니다. \"Other\" 옵션을 사용하여 선택한 키를 메타데이터에 추가할 수 있습니다." + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "최소" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "최대" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "최소 길이" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "최대 길이" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "패턴 불일치" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "정수 필요" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "실수 필요" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "필요" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "중복키를 허용하지 않습니다." + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "필터" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "사용가능한 메터데이터" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "기존 메타데이터" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "사용자 지정" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "사용가능한 메타데이터가 없습니다." + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "기존 메타데이터가 없습니다." + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "제출" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "취소" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "할당됨" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "사용 가능" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "하나 선택" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "아래의 사용 가능한 항목에서 항목을 선택" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "사용 가능한 항목 없음" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "할당된 항목을 볼 수 있도록 확장" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "사용 가능한 항목을 볼 수 있도록 확장" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "클릭하여 보기 또는 감추기" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "다시 주문할 아이템은 드래그 앤 드롭으로 가져옵니다." + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "클릭하여 세부 사항 보기" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "%(total)s 의 %(found)s 찾기" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "뒤로" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "다음" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "완료" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "불러오는 중" @@ -31,9 +175,9 @@ msgstr "데이터가 없습니다." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "오류가 발생했습니다. 나중에 다시 시도하십시오." @@ -42,16 +186,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "서버와의 통신에 문제가 발생하였으니, 다시 시도하세요." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "필터" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "파일을 읽을 수 없습니다" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "암호를 해독할 수 없습니다." @@ -61,7 +201,7 @@ #: static/horizon/js/horizon.membership.js:222 msgid "Roles" -msgstr "역할" +msgstr "Roles" #: static/horizon/js/horizon.messages.js:9 msgid "Danger: " @@ -83,16 +223,12 @@ msgid "Error: " msgstr "오류:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "취소" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "작동 중" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "양식을 제출하는 동안 오류가 발생하였습니다. 다시 시도하세요." @@ -150,7 +286,7 @@ msgstr "인스턴스 정보 자세히 보기" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "표시할 항목이 없습니다." @@ -173,14 +309,14 @@ msgid "Please confirm your selection. " msgstr "선택 사항을 확인하십시오." -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "%s 항목 표시" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "이 작업에 대한 권한이 없습니다." @@ -188,102 +324,257 @@ msgid "Passwords do not match." msgstr "비밀번호가 일치하지 않습니다." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "연결중" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "열림" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "닫는중" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "닫침" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "상태: %s" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "예" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "아니오" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "TB" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "KB" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "bytes" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "볼륨을 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "볼륨 스냅샷을 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "사용자 구성을 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "관리자 구성을 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "이미지를 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "이미지를 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "네임스페이스를 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "네임스페이스를 찾지 못했습니다." + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" -msgstr "" +msgstr "사용자를 찾지 못했습니다" #: static/horizon/js/angular/services/hz.api.keystone.js:31 msgid "Unable to create the user." -msgstr "" +msgstr "사용자를 만들지 못했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:38 msgid "Unable to delete the users." -msgstr "" +msgstr "사용자를 삭제하지못 했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:45 msgid "Unable to retrieve the user" -msgstr "" +msgstr "사용자를 찾지 못했습니다" #: static/horizon/js/angular/services/hz.api.keystone.js:53 msgid "Unable to edit the user." -msgstr "" +msgstr "사용자를 수정하지 못했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:60 msgid "Unable to delete the user." -msgstr "" +msgstr "사용자를 삭제하지 못했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:68 msgid "Unable to retrieve role" -msgstr "" +msgstr "Role을 찾지 못했습니다" #: static/horizon/js/angular/services/hz.api.keystone.js:75 msgid "Unable to create the role." -msgstr "" +msgstr "Role을 만들지 못했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:82 msgid "Unable to delete the roles." -msgstr "" +msgstr "Role을 삭제하지 못했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:89 msgid "Unable to retrieve the role" -msgstr "" +msgstr "Role을 찾지 못했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:97 msgid "Unable to edit the role." -msgstr "" +msgstr "Role을 수정하지 못했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:104 msgid "Unable to delete the role." -msgstr "" +msgstr "Role을 삭제하지 못했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:112 msgid "Unable to retrieve domains" -msgstr "" +msgstr "도메인을 찾지 못했습니다" #: static/horizon/js/angular/services/hz.api.keystone.js:119 msgid "Unable to create the domain." -msgstr "" +msgstr "도메인을 만들지 못했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:126 msgid "Unable to delete the domains." -msgstr "" +msgstr "도메인을 삭제하지 못했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:133 msgid "Unable to retrieve the domain" -msgstr "" +msgstr "도메인을 찾지 못했습니다" #: static/horizon/js/angular/services/hz.api.keystone.js:141 msgid "Unable to edit the domain." -msgstr "" +msgstr "도메인을 수정하지 못했습니다." #: static/horizon/js/angular/services/hz.api.keystone.js:148 msgid "Unable to delete the domain." -msgstr "" +msgstr "도메인을 삭제하지 못했습니다." -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" -msgstr "" +msgstr "프로젝트를 찾지 못했습니다" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." -msgstr "" +msgstr "프로젝트를 만들지 못했습니다." -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." -msgstr "" +msgstr "프로젝트를 삭제하지 못했습니다." -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" -msgstr "" +msgstr "프로젝트를 찾지 못했습니다" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." -msgstr "" +msgstr "프로젝트를 수정하지 못했습니다." -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." -msgstr "" +msgstr "프로젝트를 삭제하지 못했습니다." -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." -msgstr "" +msgstr "Role을 부여하지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "서비스 카타로그를 가져올 수 없습니다." + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "네트워크를 찾지 못 했습니다." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "네트워크를 생성하지 못하였습니다." + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "서브넷을 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "서브넷을 생성하지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "포트를 검색하지 못했습니다" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Keypair를 찾을 수 없습니다." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "Keypair를 생성하지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "가용성 존에 대한 정보를 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "제한을 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "서버를 생성하지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "서버를 찾지 못했습니다" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "확장을 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "Flavor를 찾지 못 했습니다." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "Flavor를 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "Flavor 확장 스팩을 찾지 못했습니다." + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "정책 확인 실패함." + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "시큐리티 그룹을 찾지 못했습니다." diff -Nru horizon-2015.1~b2/horizon/locale/ko_KR/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/ko_KR/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/ko_KR/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/ko_KR/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -11,9 +11,9 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-03 20:23-0600\n" -"PO-Revision-Date: 2015-02-03 20:43+0000\n" -"Last-Translator: Sungjin Kang \n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" +"Last-Translator: openstackjenkins \n" "Language-Team: Korean (Korea) (http://www.transifex.com/projects/p/horizon/language/ko_KR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -21,7 +21,7 @@ "Language: ko_KR\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "기타" @@ -39,12 +39,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "이름 \"%(name)s\"과 %(resource)s이 이미 존재합니다." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "권한이 없습니다: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "권한이 없습니다. 다시 로그인 해주십시오." @@ -82,7 +82,7 @@ msgstr "제출" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:232 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "취소" @@ -90,8 +90,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -310,44 +308,7 @@ msgid "Volume Storage" msgstr "볼륨 스토리지" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\n 당신은 왼쪽 열부터 오른쪽 열까지 항목을 추가하여 메타 데이터를 지정할 수 있습니다.\n당신은 glance 목록이나 당신의 \"다른\" 선택으로 사용할 메터 데이터를 선택할 수 있습니다." - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "활용할 수 있는 메타데이터" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "키 중복이 허용되지 않음" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:193 -msgid "No available metadata" -msgstr "사용할 수 있는 메타데이터 없음" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "존재하는 메타데이터" - -#: templates/horizon/common/_modal_form_update_metadata.html:209 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\n 당신은 왼쪽 열부터 오른쪽 열까지 자원의 메타 데이터를 이동하면서 지정할 수 있습니다. 왼쪽 열에는 Glance 메터 데이터 카탈로그에 정의된 메타 데이터가 있습니다. \"Other\" 옵션을 사용해서 당신이 선택한 키 값을 메터 데이터에 추가하세요. \n " - -#: templates/horizon/common/_modal_form_update_metadata.html:231 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "저장" @@ -422,26 +383,26 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "제한 없음" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "사용 가능" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d 바이트" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -451,17 +412,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0 Bytes" @@ -477,26 +438,34 @@ msgid_plural "Sold Puppies" msgstr[0] "강아지 판매함" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "페이크" + #: utils/filters.py:49 msgid "Never" msgstr "없음" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "유효하지 않은 포트 번호" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "유효하지 않은 IP 프로토콜 번호" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "포트 범위에서 콜론은 하나만 사용할 수 있습니다." -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "포트 번호는 정수이어야 합니다." +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "문자열은 ASCII 인쇄 문자를 포함할 수 있습니다." + #: workflows/base.py:71 msgid "Processing..." msgstr "작업 중..." diff -Nru horizon-2015.1~b2/horizon/locale/nl_NL/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/nl_NL/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/nl_NL/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/nl_NL/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Dutch (Netherlands) (http://www.transifex.com/projects/p/horizon/language/nl_NL/)\n" "MIME-Version: 1.0\n" @@ -18,8 +18,152 @@ "Language: nl_NL\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filter" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Indienen" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Annuleren" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Beschikbaar" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Terug" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Volgende" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Aan het laden" @@ -30,9 +174,9 @@ msgstr "Geen gegevens beschikbaar." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Er is een fout opgetreden. Probeert u het later nog eens." @@ -41,16 +185,12 @@ 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." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filter" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Kon het bestand niet lezen" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Kon het wachtwoord niet ontcijferen" @@ -82,16 +222,12 @@ msgid "Error: " msgstr "Fout: " -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Annuleren" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Bezig" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 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." @@ -149,7 +285,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Geen artikelen om weer te geven." @@ -172,7 +308,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -180,7 +316,7 @@ msgstr[1] "%s items weergeven" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Niet gemachtigd om deze actie uit te voeren." @@ -188,6 +324,89 @@ msgid "Passwords do not match." msgstr "Wachtwoorden komen niet overeen." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Niet in staat om de momentopnamen van het volume op te halen." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Niet in staat om de afbeelding op te halen." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Niet in staat om de afbeeldingen op te halen." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -260,30 +479,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "Niet in staat om de netwerken op te halen." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Niet in staat om de sleutelparen op te halen." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "Niet in staat om de beschikbaarheidszones op te halen." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "Niet in staat om de smaken op te halen." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Niet in staat om de beveiligingsgroepen op te halen." diff -Nru horizon-2015.1~b2/horizon/locale/nl_NL/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/nl_NL/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/nl_NL/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/nl_NL/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Dutch (Netherlands) (http://www.transifex.com/projects/p/horizon/language/nl_NL/)\n" "MIME-Version: 1.0\n" @@ -17,7 +17,7 @@ "Language: nl_NL\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Overig" @@ -35,12 +35,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Onbevoegd: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Onbevoegd. Meldt u zich opnieuw aan." @@ -78,7 +78,7 @@ msgstr "Indienen" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Annuleren" @@ -86,8 +86,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -307,44 +305,7 @@ msgid "Volume Storage" msgstr "Volumeopslag" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Opslaan" @@ -421,27 +382,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Geen limiet" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Beschikbaar" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "" msgstr[1] "" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -451,17 +412,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" @@ -479,26 +440,34 @@ msgstr[0] "" msgstr[1] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Nooit" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Geen geldig poortnummer" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Geen geldig IP protocolnummer" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Een dubbele punt is toegestaan in het poortbereik" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Poortnummer moet een geheel getal zijn" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Bezig met verwerken..." diff -Nru horizon-2015.1~b2/horizon/locale/pa_IN/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/pa_IN/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/pa_IN/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/pa_IN/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Panjabi (Punjabi) (India) (http://www.transifex.com/projects/p/horizon/language/pa_IN/)\n" "MIME-Version: 1.0\n" @@ -17,8 +17,152 @@ "Language: pa_IN\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "ਛਾਣਨੀ (ਫਿਲਟਰ)" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "ਦਾਖਲ ਕਰੋ" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "ਰੱਦ ਕਰੋ" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "ਉਪਲੱਬਧ" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "ਪਿੱਛੇ" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "ਅਗਲਾ" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "ਲੋਡ ਹੋ ਰਿਹਾ" @@ -29,9 +173,9 @@ msgstr "ਕੋਈ ਡਾਟਾ ਉਪਲਖ਼ਬਧ ਨਹੀ।" #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "ਇੱਕ ਗਲਤੀ ਵਾਪਰੀ। ਕਿਰਪਾ ਕਰ ਕੇ ਬਾਅਦ ਵਿੱਚ ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" @@ -40,16 +184,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "ਸਰਵਰ ਨਾਲ ਸੰਚਾਰ ਕਰਨ ਵੇਲੇ ਇੱਕ ਮੁਸ਼ਕਿਲ ਸੀ, ਕਿਰਪਾ ਕਰ ਕੇ ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "ਛਾਣਨੀ (ਫਿਲਟਰ)" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "ਫਾਈਲ ਪੜ੍ਹ ਨਹੀਂ ਸਕਿਆ" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "ਪਛਾਣ-ਸ਼ਬਦ ਨੂੰ ਡੀ-ਕ੍ਰਿਪਟ ਨਹੀਂ ਕਰ ਸਕਿਆ" @@ -81,16 +221,12 @@ msgid "Error: " msgstr "ਗਲਤੀ: " -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "ਰੱਦ ਕਰੋ" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "ਕੰਮ ਕਰ ਰਿਹਾ" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "ਫਾਰਮ ਸਮਰਪਣ ਵੇਲੇ ਇੱਕ ਗਲਤੀ ਸੀ। ਕਿਰਪਾ ਕਰ ਕੇ ਫਿਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" @@ -148,7 +284,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "ਪਰਦਰਸ਼ਿਤ ਕਰਨ ਲਈ ਕੋਈ ਚੀਜ਼ ਨਹੀਂ।" @@ -171,7 +307,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -179,7 +315,7 @@ msgstr[1] "%s ਚੀਜ਼ਾਂ ਵਿਖਾਈਆਂ ਜਾ ਰਹੀਆਂ" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "ਇਹ ਕਾਰਵਾਈ ਕਰਨ ਲਈ ਅਧਿਕਾਰਤ ਨਹੀੰ ਕੀਤਾ ਹੋਇਆ।" @@ -187,6 +323,89 @@ msgid "Passwords do not match." msgstr "ਪਛਾਣ-ਸ਼ਬਦ ਮੇਲ ਨਹੀੰ ਖਾਂਦੇ।" +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -259,30 +478,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "" diff -Nru horizon-2015.1~b2/horizon/locale/pa_IN/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/pa_IN/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/pa_IN/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/pa_IN/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Panjabi (Punjabi) (India) (http://www.transifex.com/projects/p/horizon/language/pa_IN/)\n" "MIME-Version: 1.0\n" @@ -18,7 +18,7 @@ "Language: pa_IN\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "ਹੋਰ" @@ -36,12 +36,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "ਇੱਕ %(resource)s ਪਹਿਲਾਂ ਹੀ \"%(name)s\" ਨਾਂ ਨਾਲ ਮੌਜੂਦ ਹੈ।" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "ਅਣ-ਅਧਿਕਾਰਤ ਕੀਤਾ: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "ਅਣਅਧਿਕਾਰਤ। ਕਿਰਪਾ ਕਰ ਕੇ ਦੁਬਾਰਾ ਲਾਗਇਨ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" @@ -79,7 +79,7 @@ msgstr "ਦਾਖਲ ਕਰੋ" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "ਰੱਦ ਕਰੋ" @@ -87,8 +87,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -308,44 +306,7 @@ msgid "Volume Storage" msgstr "ਆਇਤਨ ਭੰਡਾਰਣ" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\n ਤੁਸੀਂ ਖੱਬੀ ਪੱਟੀ ਤੋਂ ਸੱਜੀ ਪੱਟੀ ਵਿੱਚ ਚੀਜ਼ਾਂ ਜੋੜ ਕੇ ਮੈਟਾਡਾਟਾ ਨਿਰਧਾਰਤ ਕਰ ਸਕਦੇ ਹੋ।\n ਤੁਸੀਂ ਗਲਾਂਸ ਸ਼ਬਦਕੋਸ਼ ਵਿੱਚ ਜੋੜੇ ਮੈਟਾਡਾਟਾ ਨੂੰ ਚੁਣ ਸਕਦੇ ਹੋ ਜਾਂ ਫਿਰ ਤੁਸੀਂ ਆਪਣੀ\n ਪਸੰਦ ਦੀ ਕੋਈ ਚਾਬੀ ਵਰਤ ਕੇ \"ਹੋਰ\" ਚੋਣ ਵਰਤ\n ਸਕਦੇ ਹੋ।\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "ਉਪਲੱਬਧ ਮੈਟਾਡਾਟਾ" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "ਨਕਲ ਚਾਬੀਆਂ ਪਰਵਾਨਤ ਨਹੀਂ ਹਨ" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "ਮੌਜੂਦਾ ਮੈਟਾਡਾਟਾ" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\n ਤੁਸੀਂ ਖੱਬੀ ਪੱਟੀ ਤੋਂ ਸੱਜੀ ਪੱਟੀ ਵਿੱਚ ਚੀਜ਼ਾਂ ਜੋੜ ਕੇ ਵਸੀਲਾ ਮੈਟਾਡਾਟਾ ਨਿਰਧਾਰਤ ਕਰ ਸਕਦੇ ਹੋ।\n ਖੱਬੀ ਪੱਟੀ ਵਿੱਚ ਗਲਾਂਸ ਮੈਟਾਡਾਟਾ ਕੈਟਾਲੌਗ ਤੋਂ ਮੈਟਾਡਾਟਾ ਪਰਿਭਾਸ਼ਾਵਾਂ ਦਿੱਤੀਆਂ ਹੋਈਆਂ ਹਨ।\n ਆਪਣੀ ਪਸੰਦ ਦੀ ਚਾਬੀ ਨਾਲ ਮੈਟਾਡਾਟਾ ਜੋੜਨ ਲਈ \"ਹੋਰ\" ਚੋਣ ਨੂੰ ਵਰਤੋ।\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "ਸੰਭਾਲੋ" @@ -422,27 +383,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "ਕੋਈ ਹੱਦ ਨਹੀਂ" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "ਉਪਲੱਬਧ" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d ਬਾਈਟ" msgstr[1] "%(size)d ਬਾਈਟਾਂ" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -452,17 +413,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" @@ -480,26 +441,34 @@ msgstr[0] "" msgstr[1] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "ਕਦੇ ਨਹੀਂ" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "ਇੱਕ ਢੁਕਵਾਂ ਪੋਰਟ ਨੰਬਰ ਨਹੀਂ" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "ਇੱਕ ਢੁਕਵਾਂ IP ਜਾਬਤਾ ਅੰਕ ਨਹੀਂ" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "ਪੋਰਟ ਹੱਦ ਵਿੱਚ ਇੱਕ ਕੌਲਨ ਦੀ ਪਰਵਾਨਗੀ ਹੈ" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "ਪੋਰਟ ਨੰਬਰ ਦਾ ਅੰਕ ਹੋਣਾ ਲਾਜਮੀ ਹੈ" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "...ਕਾਰਵਾਈ ਚੱਲ ਰਹੀ ਹੈ" diff -Nru horizon-2015.1~b2/horizon/locale/pl_PL/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/pl_PL/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/pl_PL/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/pl_PL/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Polish (Poland) (http://www.transifex.com/projects/p/horizon/language/pl_PL/)\n" "MIME-Version: 1.0\n" @@ -17,8 +17,152 @@ "Language: pl_PL\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filtr" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Wyślij" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Anuluj" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Dostępne" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Wstecz" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Następny" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Wczytywanie" @@ -29,9 +173,9 @@ msgstr "Brak dostępnych danych." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Wystąpił błąd. Proszę spróbować później." @@ -40,16 +184,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "Wystąpił problem w komunikacji z serwerem, proszę spróbować ponownie." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filtr" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Nie można odczytać pliku" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Nie można odszyfrować hasła" @@ -81,16 +221,12 @@ msgid "Error: " msgstr "Błąd:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Anuluj" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Praca" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "Wystąpił błąd podczas wysyłania formularza. Proszę spróbować ponownie." @@ -148,7 +284,7 @@ msgstr "Wyświetl szczegóły instancji" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Brak pozycji do wyświetlenia." @@ -171,7 +307,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -180,7 +316,7 @@ msgstr[2] "" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Nie upoważniono do przeprowadzenie tej czynności." @@ -188,6 +324,89 @@ msgid "Passwords do not match." msgstr "Hasła nie pasują" +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "Tak" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "Nie" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Nie można pobrać migawek wolumenów." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Nie można pobrać obrazu." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Nie można pobrać obrazów" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -260,30 +479,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "Nie można pobrać sieci." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Nie można pobrać par kluczy." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "Nie można pobrać stref dostępności." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "Nie można pobrać odmian." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Nie można pobrać grup zabezpieczeń." diff -Nru horizon-2015.1~b2/horizon/locale/pl_PL/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/pl_PL/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/pl_PL/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/pl_PL/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Polish (Poland) (http://www.transifex.com/projects/p/horizon/language/pl_PL/)\n" "MIME-Version: 1.0\n" @@ -18,7 +18,7 @@ "Language: pl_PL\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Inne" @@ -36,12 +36,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "%(resource)s o nazwie \"%(name)s\" już istnieje." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Nieupoważniono: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Brak uprawnień. Proszę spróbować się wylogować i zalogować ponownie." @@ -79,7 +79,7 @@ msgstr "Wyślij" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Anuluj" @@ -87,8 +87,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -309,44 +307,7 @@ msgid "Volume Storage" msgstr "Przechowywanie danych" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\nMożna podać metadane poprzez dodanie pozycji z lewej kolumny\ndo prawej kolumny. Można wybrać metadane dodane do słownika glance lub wykorzystać opcję „Inne” by podać własny klucz." - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Dostępne metadane" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Ponowne użycie nazwy klucza jest niedozwolone." - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "Metadane niedostępne" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Istniejące metadane" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\nMożna podać metadane zasobów poprzez przesunięcie pozycji z lewej\nkolumny do prawej. W lewej kolumnie znajdują się definicje metadanych z Katalogu Metadanych Glance. W celu podania własnego klucza\nnależy wykorzystać opcję „Inne”." - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Zapisz" @@ -425,15 +386,15 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Bez ograniczeń" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Dostępne" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" @@ -441,12 +402,12 @@ msgstr[1] "%(size)d bajty" msgstr[2] "%(size)d bajtów" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -456,17 +417,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0 bajtów" @@ -486,26 +447,34 @@ msgstr[1] "Sprzedano pieski" msgstr[2] "Sprzedano pieski" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Nigdy" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Błędny numer portu" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Błędny numer protokołu IP" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Tylko jeden dwukropek jest dozwolony w zakresie portów" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Numer portu musi być liczbą całkowitą" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Przetwarzanie…" diff -Nru horizon-2015.1~b2/horizon/locale/pt/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/pt/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/pt/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/pt/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Portuguese (http://www.transifex.com/projects/p/horizon/language/pt/)\n" "MIME-Version: 1.0\n" @@ -17,8 +17,152 @@ "Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filtro" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Submeter" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Cancelar" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Disponível" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Voltar" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Seguinte" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "A carregar" @@ -29,9 +173,9 @@ msgstr "Não há dados disponíveis." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Ocorreu um erro. Por favor, tente mais tarde." @@ -40,16 +184,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "Ocorreu um problema ao comunicar com o servidor, por favor, tente novamente." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filtro" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Não foi possivel ler o arquivo" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Não foi possível descriptografar a senha" @@ -81,16 +221,12 @@ msgid "Error: " msgstr "Erro: " -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Cancelar" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "A processar" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "Ocorreu um erro ao enviar o formulário. Por favor, tente novamente." @@ -148,7 +284,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Não há itens para exibir." @@ -171,7 +307,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -179,7 +315,7 @@ msgstr[1] "" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Não tem autorizacao para realizar esta operação." @@ -187,6 +323,89 @@ msgid "Passwords do not match." msgstr "As senhas não são iguais." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "Sim" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -259,30 +478,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "" diff -Nru horizon-2015.1~b2/horizon/locale/pt/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/pt/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/pt/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/pt/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Portuguese (http://www.transifex.com/projects/p/horizon/language/pt/)\n" "MIME-Version: 1.0\n" @@ -18,7 +18,7 @@ "Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Outros" @@ -36,12 +36,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Não autorizado: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Não autorizado. Por favor, tente iniciar a sessão novamente." @@ -79,7 +79,7 @@ msgstr "Submeter" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Cancelar" @@ -87,8 +87,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -308,44 +306,7 @@ msgid "Volume Storage" msgstr "Volume Armazenamento" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Metadados Disponíveis" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Não são permitidas chaves duplicadas" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Metadados Existentes" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Guardar" @@ -422,27 +383,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Sem Limite" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Disponível" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "" msgstr[1] "" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -452,17 +413,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" @@ -480,26 +441,34 @@ msgstr[0] "" msgstr[1] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Nunca" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Não é um número de porta válido" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Não é um número de protocolo de IP válido" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "São permitidos dois pontos no limite da porta" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "O número da porta deve ser um íntegro" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "A processar ..." diff -Nru horizon-2015.1~b2/horizon/locale/pt_BR/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/pt_BR/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/pt_BR/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/pt_BR/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -3,13 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Gabriel Wainer, 2015 # Rodrigo Felix de Almeida , 2014 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/horizon/language/pt_BR/)\n" "MIME-Version: 1.0\n" @@ -18,8 +19,152 @@ "Language: pt_BR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filtro" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Enviar" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Cancelar" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Disponível" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Voltar" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Próximo" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "Encerrar" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Carregando" @@ -30,9 +175,9 @@ msgstr "Não há dados disponíveis." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Um erro ocorreu. Por favor tente novamente mais tarde." @@ -41,16 +186,12 @@ 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." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filtro" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Não foi possivel ler o arquivo" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Não foi possível descriptografar a senha" @@ -82,16 +223,12 @@ msgid "Error: " msgstr "Erro:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Cancelar" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Trabalhando" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "Houve um erro ao enviar o formulário. Por favor tente novamente." @@ -149,7 +286,7 @@ msgstr "Ver Detalhes da Instância" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Sem itens para exibir." @@ -170,9 +307,9 @@ #: static/horizon/js/horizon.tables.js:204 msgid "Please confirm your selection. " -msgstr "" +msgstr "Por favor confirme a sua seleção." -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -180,7 +317,7 @@ msgstr[1] "Exibindo %s itens" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Não autorizado para realizar esta operação." @@ -188,102 +325,257 @@ msgid "Passwords do not match." msgstr "As senhas não conferem." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "Sim" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "Não" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Não é possível recuperar snapshots de volume." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Não foi possível recuperar a imagem." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Não foi possível obter as imagems" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" -msgstr "" +msgstr "Não foi possível obter os usuários" #: static/horizon/js/angular/services/hz.api.keystone.js:31 msgid "Unable to create the user." -msgstr "" +msgstr "Não foi possível criar o usuário." #: static/horizon/js/angular/services/hz.api.keystone.js:38 msgid "Unable to delete the users." -msgstr "" +msgstr "Não foi possível excluir os usuários." #: static/horizon/js/angular/services/hz.api.keystone.js:45 msgid "Unable to retrieve the user" -msgstr "" +msgstr "Não foi possível obter o usuário" #: static/horizon/js/angular/services/hz.api.keystone.js:53 msgid "Unable to edit the user." -msgstr "" +msgstr "Não foi possível editar o usuário." #: static/horizon/js/angular/services/hz.api.keystone.js:60 msgid "Unable to delete the user." -msgstr "" +msgstr "Não foi possível excluir o usuário." #: static/horizon/js/angular/services/hz.api.keystone.js:68 msgid "Unable to retrieve role" -msgstr "" +msgstr "Não foi possível obter o papel" #: static/horizon/js/angular/services/hz.api.keystone.js:75 msgid "Unable to create the role." -msgstr "" +msgstr "Não foi possível criar o papel." #: static/horizon/js/angular/services/hz.api.keystone.js:82 msgid "Unable to delete the roles." -msgstr "" +msgstr "Não foi possível excluir os papéis." #: static/horizon/js/angular/services/hz.api.keystone.js:89 msgid "Unable to retrieve the role" -msgstr "" +msgstr "Não foi possível obter o papel" #: static/horizon/js/angular/services/hz.api.keystone.js:97 msgid "Unable to edit the role." -msgstr "" +msgstr "Não foi possível editar o papel." #: static/horizon/js/angular/services/hz.api.keystone.js:104 msgid "Unable to delete the role." -msgstr "" +msgstr "Não foi possível excluir o papel." #: static/horizon/js/angular/services/hz.api.keystone.js:112 msgid "Unable to retrieve domains" -msgstr "" +msgstr "Não foi possível obter os domínios" #: static/horizon/js/angular/services/hz.api.keystone.js:119 msgid "Unable to create the domain." -msgstr "" +msgstr "Não foi possível criar o domínio." #: static/horizon/js/angular/services/hz.api.keystone.js:126 msgid "Unable to delete the domains." -msgstr "" +msgstr "Não foi possível excluir os domínios." #: static/horizon/js/angular/services/hz.api.keystone.js:133 msgid "Unable to retrieve the domain" -msgstr "" +msgstr "Não foi possível obter o domínio" #: static/horizon/js/angular/services/hz.api.keystone.js:141 msgid "Unable to edit the domain." -msgstr "" +msgstr "Não foi possível editar o domínio." #: static/horizon/js/angular/services/hz.api.keystone.js:148 msgid "Unable to delete the domain." -msgstr "" +msgstr "Não foi possível excluir o domínio." -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" -msgstr "" +msgstr "Não foi possível obter os projetos" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." -msgstr "" +msgstr "Não foi possível criar o projeto." -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." -msgstr "" +msgstr "Não foi possível excluir os projetos." -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" -msgstr "" +msgstr "Não foi possível obter o projeto" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." -msgstr "" +msgstr "Não foi possível editar o projeto." -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." -msgstr "" +msgstr "Não foi possível excluir o projeto." -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." +msgstr "Não foi possível permitir o papel." + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "Não é possível recuperar redes." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Não é possível recuperar pares de chaves." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "Não é possível recuperar todas zonas de disponibilidade." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "Não foi possível recuperar flavors." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Não é possível recuperar grupos de segurança" diff -Nru horizon-2015.1~b2/horizon/locale/pt_BR/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/pt_BR/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/pt_BR/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/pt_BR/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -3,13 +3,15 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Fernando F. Rodrigues , 2015 +# Gabriel Wainer, 2015 # Rodrigo Felix de Almeida , 2014 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/horizon/language/pt_BR/)\n" "MIME-Version: 1.0\n" @@ -18,7 +20,7 @@ "Language: pt_BR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Outro" @@ -36,12 +38,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "Um %(resource)s com o nome \"%(name)s\" já existe." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Não autorizado: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Não autorizado. Por favor tente efetuar login novamente." @@ -51,7 +53,7 @@ #: browsers/base.py:88 msgid "Navigation Item" -msgstr "Ítem de Navegação" +msgstr "Item de Navegação" #: browsers/views.py:41 #, python-format @@ -79,7 +81,7 @@ msgstr "Enviar" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Cancelar" @@ -87,8 +89,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -96,7 +96,7 @@ #: tables/actions.py:645 msgid "This action cannot be undone." -msgstr "" +msgstr "Esta ação não pode ser desfeita." #: tables/actions.py:767 #, python-format @@ -152,11 +152,11 @@ #: tables/base.py:361 #, python-format msgid "The attribute %(attr)s doesn't exist on %(obj)s." -msgstr "O Atributo %(attr)s não existe em %(obj)s." +msgstr "O atributo %(attr)s não existe em %(obj)s." #: tables/base.py:990 msgid "No items to display." -msgstr "Sem ítens para exibir." +msgstr "Sem itens para exibir." #: tables/base.py:1099 #: templates/horizon/common/_data_table_table_actions.html:47 @@ -294,7 +294,7 @@ #: templates/horizon/common/_limit_summary.html:29 #, python-format msgid "Allocated %(used)s of %(available)s " -msgstr "" +msgstr "Alocado %(used)s de %(available)s " #: templates/horizon/common/_limit_summary.html:35 msgid "Security Groups" @@ -308,44 +308,7 @@ msgid "Volume Storage" msgstr "Armazenamento de volume" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\nVocê pode especificar metadados, adicionando itens da coluna da esquerda para\na coluna da direita. Você pode selecionar o metadados adicionados para o dicionário do glance\nou você pode usar a opção \"Outro\" utilizando uma chave\nde sua escolha." - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Metadados disponível" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Chaves duplicadas não são permitidas" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "Metadados não disponível" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Metadados existente" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\nVocê pode especificar recursos de metadados movimentando itens da coluna da esquerda \npara a coluna da direita. Na coluna da esquerda há \ndefinições de metadados do Catalogo de Metadados do Glance. Utilize a opção \"Outro\" \npara adicionar metadados com a chave de sua escolha. " - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Salvar" @@ -408,7 +371,7 @@ #: templates/horizon/common/_usage_summary.html:24 msgid "This Period's RAM-Hours:" -msgstr "" +msgstr "Quantidade de RAM-Horas deste período:" #: templates/horizon/common/_workflow.html:40 msgid "Back" @@ -422,27 +385,27 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Sem Limite" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Disponível" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d Byte" msgstr[1] "%(size)d Bytes" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -452,17 +415,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0 Bytes" @@ -480,26 +443,34 @@ msgstr[0] "Puppy Vendido" msgstr[1] "Puppies Vendidos" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "Falso" + #: utils/filters.py:49 msgid "Never" msgstr "Nunca" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Não é um número de porta válido" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Não é um número de protocolo IP válido" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Uma pontução de dois pontos permitida no intervalo de portas" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Número de porta deve ser inteiro" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Processando..." diff -Nru horizon-2015.1~b2/horizon/locale/ru/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/ru/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/ru/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/ru/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Russian (http://www.transifex.com/projects/p/horizon/language/ru/)\n" "MIME-Version: 1.0\n" @@ -18,8 +18,152 @@ "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" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Фильтр" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Отправить" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Отмена" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Доступно" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Назад" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Следующий" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Загрузка" @@ -30,9 +174,9 @@ msgstr "Нет данных." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Произошла ошибка. Повторите попытку." @@ -41,16 +185,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "Обнаружена проблема при соединении с сервером. Повторите попытку." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Фильтр" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Не удалось прочитать файл" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Не удалось расшифровать пароль" @@ -82,16 +222,12 @@ msgid "Error: " msgstr "Ошибка:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Отмена" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Обработка" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "При отправке формы произошла ошибка. Повторите попытку." @@ -149,7 +285,7 @@ msgstr "Посмотреть информацию об инстансе" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Нет элементов для отображения." @@ -172,7 +308,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -181,7 +317,7 @@ msgstr[2] "Отображено %s значений" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Нет прав для выполнения." @@ -189,6 +325,89 @@ msgid "Passwords do not match." msgstr "Пароли не совпадают." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "Да" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "Нет" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "ГБ" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "МБ" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Не удалось получить снимки диска." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Не удалось получить образ." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Не удалось получить образы." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -261,30 +480,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "Не удалось получить сети." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Невозможно получить ключевые пары." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "Не удалось получить зоны доступности." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "Не удалось получить схемы." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Не удалось получить группы безопасности." diff -Nru horizon-2015.1~b2/horizon/locale/ru/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/ru/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/ru/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/ru/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -3,12 +3,13 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Ilya Alekseyev , 2015 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Russian (http://www.transifex.com/projects/p/horizon/language/ru/)\n" "MIME-Version: 1.0\n" @@ -17,7 +18,7 @@ "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" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Другое" @@ -33,14 +34,14 @@ #: exceptions.py:163 #, python-format msgid "A %(resource)s with the name \"%(name)s\" already exists." -msgstr "%(resource)s с названием \"%(name)s\" уже существует." +msgstr "%(resource)s с именем\"%(name)s\" уже существует." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Не авторизован: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Вы не авторизованы. Попробуйте войти в систему еще раз." @@ -71,14 +72,14 @@ #: forms/fields.py:66 msgid "Invalid subnet mask" -msgstr "Неправльная маска подсети" +msgstr "Неправильная маска подсети" #: forms/views.py:132 templates/horizon/common/_usage_summary.html:16 msgid "Submit" msgstr "Отправить" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Отмена" @@ -86,8 +87,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -95,7 +94,7 @@ #: tables/actions.py:645 msgid "This action cannot be undone." -msgstr "" +msgstr "Это действие не может быть отменено." #: tables/actions.py:767 #, python-format @@ -116,7 +115,7 @@ #: tables/actions.py:832 #, python-format msgid "You are not allowed to %(action)s: %(objs)s" -msgstr "Не разрешено: %(action)s: %(objs)s" +msgstr "Вам не разрешено выполнение: %(action)s: %(objs)s" #: tables/actions.py:839 #, python-format @@ -174,7 +173,7 @@ #: templates/_header.html:5 #, python-format msgid "Logged in as: %(username)s" -msgstr "Вошёл как: %(username)s" +msgstr "Пользователь: %(username)s" #: templates/_header.html:7 msgid "Help" @@ -231,9 +230,9 @@ #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "Показан %(counter)s элемент" +msgstr[1] "Показано %(counter)s элементов" +msgstr[2] "Показано %(counter)s элементов" #: templates/horizon/common/_data_table.html:77 msgid "« Prev" @@ -250,7 +249,7 @@ #: templates/horizon/common/_domain_page_header.html:6 #, python-format msgid "%(context_name)s:" -msgstr "" +msgstr "%(context_name)s:" #: templates/horizon/common/_formset_table.html:35 msgid "Add a row" @@ -259,7 +258,7 @@ #: templates/horizon/common/_formset_table_row.html:15 #, python-format msgid "%(name)s: %(error)s" -msgstr "" +msgstr "%(name)s: %(error)s" #: templates/horizon/common/_limit_summary.html:4 msgid "Limit Summary" @@ -294,7 +293,7 @@ #: templates/horizon/common/_limit_summary.html:29 #, python-format msgid "Allocated %(used)s of %(available)s " -msgstr "" +msgstr "Выделено %(used)s из %(available)s " #: templates/horizon/common/_limit_summary.html:35 msgid "Security Groups" @@ -308,44 +307,7 @@ msgid "Volume Storage" msgstr "Хранилище" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\nВы можете указать метаданные путём добавления объектов из левой колонки в \nправую колонку. Вы можете выбрать метаданные добавленные в glance\n словарь или вы можете использовать \"Другую\" опцию с помощью клавиши на\n ваш выбор" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Доступные метаданные" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Повторяющиеся ключи не допускаются" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Имеющиеся метаданные" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\nВы можете указать метаданные ресурса, перемещая элементы с левой\nколонки в правую колонку. В левом столбце есть определения\nметаданных из Glance каталога метаданных. Используйте \"Другой\" вариант,\nчтобы добавить метаданные с помощью клавиши на ваш выбор." - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Сохранить" @@ -354,17 +316,17 @@ #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "Показан %(nav_items)s элемент" +msgstr[1] "Показано %(nav_items)s элементов" +msgstr[2] "Показано %(nav_items)s элементов" #: 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] "" -msgstr[2] "" +msgstr[0] "Показан %(content_items)s элемент" +msgstr[1] "Показано %(content_items)s элементов" +msgstr[2] "Показано %(content_items)s элементов" #: templates/horizon/common/_usage_summary.html:3 msgid "Usage Summary" @@ -372,7 +334,7 @@ #: templates/horizon/common/_usage_summary.html:7 msgid "Select a period of time to query its usage:" -msgstr "" +msgstr "Выберите временной интервал для запроса использования:" #: templates/horizon/common/_usage_summary.html:9 #, python-format @@ -394,23 +356,23 @@ #: templates/horizon/common/_usage_summary.html:20 msgid "Active Instances:" -msgstr "" +msgstr "Активные инстансы:" #: templates/horizon/common/_usage_summary.html:21 msgid "Active RAM:" -msgstr "" +msgstr "Используемая RAM:" #: templates/horizon/common/_usage_summary.html:22 msgid "This Period's VCPU-Hours:" -msgstr "" +msgstr "vCPU-часов за период:" #: templates/horizon/common/_usage_summary.html:23 msgid "This Period's GB-Hours:" -msgstr "" +msgstr "ГБ-часов за период:" #: templates/horizon/common/_usage_summary.html:24 msgid "This Period's RAM-Hours:" -msgstr "" +msgstr "RAM-часов за период:" #: templates/horizon/common/_workflow.html:40 msgid "Back" @@ -424,28 +386,28 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Без ограничений" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Доступно" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%(size)d байт" +msgstr[1] "%(size)d байт" +msgstr[2] "%(size)d байт" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s КБ" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s МБ" @@ -455,19 +417,19 @@ msgid "%s GB" msgstr "%s ГБ" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s ТБ" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s ПБ" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" -msgstr "" +msgstr "0 байт" #. Translators: test code, don't really have to translate #: test/test_dashboards/dogs/puppies/tables.py:31 @@ -485,26 +447,34 @@ msgstr[1] "" msgstr[2] "Sold Puppies" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Никогда" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Недопустимый номер порта" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Недопустимый номер IP-протокола" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "В списке портов допустима одна запятая" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Номер порта должен быть целым числом" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Обработка…" diff -Nru horizon-2015.1~b2/horizon/locale/sl_SI/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/sl_SI/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/sl_SI/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/sl_SI/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Slovenian (Slovenia) (http://www.transifex.com/projects/p/horizon/language/sl_SI/)\n" "MIME-Version: 1.0\n" @@ -17,7 +17,7 @@ "Language: sl_SI\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Ostalo" @@ -35,12 +35,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Niste avtorizirani: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Neavtoriziran dostop. Poskusite se prijaviti in poskusite znova." @@ -78,7 +78,7 @@ msgstr "Potrdi" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Prekliči" @@ -86,8 +86,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -309,44 +307,7 @@ msgid "Volume Storage" msgstr "Diskovni prostor nosilca" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Shrani" @@ -427,15 +388,15 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Ni omejitve" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Na voljo" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" @@ -444,12 +405,12 @@ msgstr[2] "" msgstr[3] "" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -459,17 +420,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" @@ -491,26 +452,34 @@ msgstr[2] "" msgstr[3] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Nikoli" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Številka vrat ni veljavna" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Številka protokola IP ni veljavna." -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Ena kolona je dovoljena v razponu vrat" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Vrata navedite s številko." +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Obdelava..." diff -Nru horizon-2015.1~b2/horizon/locale/sr/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/sr/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/sr/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/sr/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Serbian (http://www.transifex.com/projects/p/horizon/language/sr/)\n" "MIME-Version: 1.0\n" @@ -17,8 +17,152 @@ "Language: sr\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" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filter" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Predaj" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "Odustani" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Dostupno" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Nazad" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "Sledeći" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Učitavanje" @@ -29,9 +173,9 @@ msgstr "Nema podataka." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Greška. Pokušajte ponovo kasnije." @@ -40,16 +184,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "Postojao je problem u vezi sa serverom, pokušajte ponovo." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filter" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Ne mogu da pročitam fajl" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Ne mogu dešifrovati lozinku" @@ -81,16 +221,12 @@ msgid "Error: " msgstr "Greška:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "Odustani" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Obrada u toku" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "Greška u predaji forme. Pokušajte ponovo." @@ -148,7 +284,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Nema stavki za prikaz." @@ -171,7 +307,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -180,7 +316,7 @@ msgstr[2] "" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Niste nadležni za ovaj zahvat." @@ -188,6 +324,89 @@ msgid "Passwords do not match." msgstr "Lozinka se ne podudara." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "Nemoguće je izvući snapshot-ove volume-a." + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "Nemoguće je dobiti image." + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "Nemoguće je dobiti image-e." + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -260,30 +479,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "Nemoguće je dopreti do mreža." + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "Nemoguće je dobiti parove ključeva." + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "Nemoguće je dobiti zone dostupnosti." + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "Nemoguće je dobiti arome." + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Nemoguće je dobiti sigurnosne grupe." diff -Nru horizon-2015.1~b2/horizon/locale/sr/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/sr/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/sr/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/sr/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Serbian (http://www.transifex.com/projects/p/horizon/language/sr/)\n" "MIME-Version: 1.0\n" @@ -17,7 +17,7 @@ "Language: sr\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" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Drugi" @@ -35,12 +35,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Neovlašćen: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Niste ovlašćeni. Molim, pokušajte da se ulogujete ponovo." @@ -78,7 +78,7 @@ msgstr "Predaj" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "Odustani" @@ -86,8 +86,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -308,44 +306,7 @@ msgid "Volume Storage" msgstr "Volume storage" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "Dostupni Metadata podaci" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "Dupli kljucevi nisu omoguceni" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "Postojeci Metadata podaci" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Sačuvaj" @@ -424,15 +385,15 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Nema ograničenja" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Dostupno" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" @@ -440,12 +401,12 @@ msgstr[1] "" msgstr[2] "" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -455,17 +416,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "" @@ -485,26 +446,34 @@ msgstr[1] "" msgstr[2] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "" + #: utils/filters.py:49 msgid "Never" msgstr "Nikad" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Nevažeći broj porta" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Nevažeći broj IP protokola" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Jedna dvotačka je dozvoljena u opsegu portova" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Broj porta mora biti celobrojna vrednost" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "Obrada..." diff -Nru horizon-2015.1~b2/horizon/locale/te_IN/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/te_IN/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/te_IN/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/te_IN/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Telugu (India) (http://www.transifex.com/projects/p/horizon/language/te_IN/)\n" "MIME-Version: 1.0\n" @@ -17,8 +17,152 @@ "Language: te_IN\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "వడియగట్టు" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "రద్దుచేయు" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "లభ్యమగును" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "లొడవుతుంది" @@ -29,9 +173,9 @@ msgstr " సమాచారం అందుబాటు లో లేదు" #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "లోపము జరిగినది. దయచేసి మరల ప్రయత్నించండి" @@ -40,16 +184,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "సర్వరు తొ సంభాషించుటలో లోపము జరిగినది. దయచేసి మరల ప్రయత్నించండి" -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "వడియగట్టు" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "దస్త్రం ను చదవలేము" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "సంకేత పదాన్ని డీక్రిప్షన్ చేయలేము" @@ -81,16 +221,12 @@ msgid "Error: " msgstr "లోపము" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "రద్దుచేయు" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "పనిచేస్తున్నది" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "ఫారం దాఖలుచేయుటలో లోపము ఎర్పడినది. దయచేసి తిరిగి ప్రయత్నించండి." @@ -148,7 +284,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "ప్రదర్శించుటకు ఎటువంటి అంశము/అయిటము లు లేవు" @@ -171,7 +307,7 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" @@ -179,7 +315,7 @@ msgstr[1] "" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "ఈ పని చేయుటకు అనుమతి లేదు" @@ -187,6 +323,89 @@ msgid "Passwords do not match." msgstr "సంకేత పదాలు సరిపోలడం లేదు. " +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -259,30 +478,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "" diff -Nru horizon-2015.1~b2/horizon/locale/tr_TR/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/tr_TR/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/tr_TR/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/tr_TR/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -3,13 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Mücahit Büyükyılmaz , 2015 # sercanaydogan , 2014 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Turkish (Turkey) (http://www.transifex.com/projects/p/horizon/language/tr_TR/)\n" "MIME-Version: 1.0\n" @@ -18,8 +19,152 @@ "Language: tr_TR\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "Filtre" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "Gönder" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "İptal" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "Uygun" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "Geri" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "İleri" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "Yükleniyor" @@ -30,9 +175,9 @@ msgstr "Veri yok." #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "Bir hata oluştu. Lütfen tekrar deneyin." @@ -41,22 +186,18 @@ msgid "There was a problem communicating with the server, please try again." msgstr "Sunucu ile iletişimde bir sorun var, lütfen tekrar deneyin." -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "Filtre" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "Dosya okunamadı" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "Şifre Çözülemedi" #: static/horizon/js/horizon.membership.js:190 msgid "No roles" -msgstr "Roller yok" +msgstr "Rol yok" #: static/horizon/js/horizon.membership.js:222 msgid "Roles" @@ -76,22 +217,18 @@ #: static/horizon/js/horizon.messages.js:12 msgid "Success: " -msgstr "Sonuç:" +msgstr "Başarılı:" #: static/horizon/js/horizon.messages.js:13 msgid "Error: " msgstr "Hata:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "İptal" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "Çalışıyor" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "Formu gönderirken bir hata oluştu. Lütfen tekrar deneyin." @@ -149,7 +286,7 @@ msgstr "" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "Görüntülenecek öğe yok." @@ -172,14 +309,14 @@ msgid "Please confirm your selection. " msgstr "" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "%s adet gösteriliyor" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "Bu işlemi yapmak için yetkiniz yok." @@ -187,6 +324,89 @@ msgid "Passwords do not match." msgstr "Şifreler uyuşmuyor." +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -259,30 +479,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "Güvenlik grupları alınamadı." diff -Nru horizon-2015.1~b2/horizon/locale/tr_TR/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/tr_TR/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/tr_TR/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/tr_TR/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -3,13 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Mücahit Büyükyılmaz , 2015 # sercanaydogan , 2014 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Turkish (Turkey) (http://www.transifex.com/projects/p/horizon/language/tr_TR/)\n" "MIME-Version: 1.0\n" @@ -18,7 +19,7 @@ "Language: tr_TR\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "Diğer" @@ -36,18 +37,18 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "\"%(name)s\" isimli bir %(resource)s zaten var." -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "Yetkisiz: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "Yetkisiz Giriş. Lütfen tekrar giriş yapın. " #: middleware.py:103 msgid "Session timed out." -msgstr "Oturum sona erdi" +msgstr "Oturum sona erdi." #: browsers/base.py:88 msgid "Navigation Item" @@ -60,15 +61,15 @@ #: conf/default.py:41 msgid "Password is not accepted" -msgstr "Parola kabul edilmedi" +msgstr "Şifre kabul edilmedi" #: forms/fields.py:64 msgid "Incorrect format for IP address" -msgstr "Hatalı IP adres biçemi" +msgstr "Hatalı IP adres formatı" #: forms/fields.py:65 msgid "Invalid version for IP address" -msgstr "Geçersiz IP adres sürümü" +msgstr "Geçersiz IP adres versiyonu" #: forms/fields.py:66 msgid "Invalid subnet mask" @@ -79,7 +80,7 @@ msgstr "Gönder" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "İptal" @@ -87,8 +88,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -96,7 +95,7 @@ #: tables/actions.py:645 msgid "This action cannot be undone." -msgstr "" +msgstr "Bu işlem geri alınamaz." #: tables/actions.py:767 #, python-format @@ -143,11 +142,11 @@ #: tables/actions.py:949 msgid "Updated" -msgstr "Güncellenmiş" +msgstr "Güncellendi" #: tables/base.py:305 msgid "-" -msgstr "" +msgstr "-" #: tables/base.py:361 #, python-format @@ -161,7 +160,7 @@ #: tables/base.py:1099 #: templates/horizon/common/_data_table_table_actions.html:47 msgid "Actions" -msgstr "Eylemler" +msgstr "İşlemler" #: tables/base.py:1329 #, python-format @@ -202,11 +201,11 @@ #: templates/auth/_login.html:32 msgid "Sign In" -msgstr "Kaydol" +msgstr "Oturum Aç" #: templates/auth/login.html:4 msgid "Login" -msgstr "Bağlan" +msgstr "Giriş" #: templates/horizon/_messages.html:7 msgid "Info: " @@ -218,7 +217,7 @@ #: templates/horizon/_messages.html:19 msgid "Success: " -msgstr "Sonuç:" +msgstr "Başarılı:" #: templates/horizon/_messages.html:25 msgid "Error: " @@ -232,24 +231,24 @@ #, python-format msgid "Displaying %(counter)s item" msgid_plural "Displaying %(counter)s items" -msgstr[0] "" +msgstr[0] "%(counter)s öge görüntüleniyor" #: templates/horizon/common/_data_table.html:77 msgid "« Prev" -msgstr "" +msgstr "« Geri" #: templates/horizon/common/_data_table.html:80 msgid "Next »" -msgstr "" +msgstr "İleri »" #: templates/horizon/common/_data_table_table_actions.html:45 msgid "More Actions" -msgstr "" +msgstr "Diğer İşlemler" #: templates/horizon/common/_domain_page_header.html:6 #, python-format msgid "%(context_name)s:" -msgstr "" +msgstr "%(context_name)s:" #: templates/horizon/common/_formset_table.html:35 msgid "Add a row" @@ -258,7 +257,7 @@ #: templates/horizon/common/_formset_table_row.html:15 #, python-format msgid "%(name)s: %(error)s" -msgstr "" +msgstr "%(name)s: %(error)s" #: templates/horizon/common/_limit_summary.html:4 msgid "Limit Summary" @@ -307,44 +306,7 @@ msgid "Volume Storage" msgstr "Disk Depolama Birimi" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "Kaydet" @@ -353,13 +315,13 @@ #, python-format msgid "Displaying %(nav_items)s item" msgid_plural "Displaying %(nav_items)s items" -msgstr[0] "" +msgstr[0] "%(nav_items)s öge görüntüleniyor" #: 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[0] "%(content_items)s öge görüntüleniyor" #: templates/horizon/common/_usage_summary.html:3 msgid "Usage Summary" @@ -374,14 +336,14 @@ msgid "" "\n" " %(start)s" -msgstr "" +msgstr "\n %(start)s" #: templates/horizon/common/_usage_summary.html:13 #, python-format msgid "" "\n" " %(end)s" -msgstr "" +msgstr "\n%(end)s" #: templates/horizon/common/_usage_summary.html:17 msgid "The date should be in YYYY-mm-dd format." @@ -419,26 +381,26 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "Limitsiz" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "Uygun" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" -msgstr[0] "" +msgstr[0] "%(size)d Byte" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -448,19 +410,19 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" -msgstr "" +msgstr "0 Byte" #. Translators: test code, don't really have to translate #: test/test_dashboards/dogs/puppies/tables.py:31 @@ -474,26 +436,34 @@ msgid_plural "Sold Puppies" msgstr[0] "" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "Sahte" + #: utils/filters.py:49 msgid "Never" msgstr "Asla" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "Geçersiz port numarası" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "Geçersiz IP Protokol numarası" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "Port aralığında tek bir kolon girilebilir" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "Port numarası bir tam sayı olmalıdır" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "İşleniyor..." diff -Nru horizon-2015.1~b2/horizon/locale/zh_CN/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/zh_CN/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/zh_CN/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/zh_CN/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -3,14 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# LIU Yulong , 2014 +# LIU Yulong , 2014-2015 # Xiao Xi LIU , 2014 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Chinese (China) (http://www.transifex.com/projects/p/horizon/language/zh_CN/)\n" "MIME-Version: 1.0\n" @@ -19,8 +19,152 @@ "Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "筛选" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "提交" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "取消" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "已分配" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "可用配额" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "选择一个" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "返回" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "下一步" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "结束" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "加载中" @@ -31,9 +175,9 @@ msgstr "没有可用数据" #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "发生错误。请稍后重试。" @@ -42,16 +186,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "与服务器通信出现问题,请再试一次。" -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "筛选" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "不能读取文件" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "不能解密密码" @@ -83,16 +223,12 @@ msgid "Error: " msgstr "错误:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "取消" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "进行中" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "在提交表单的时候出现错误,请再次尝试。" @@ -150,7 +286,7 @@ msgstr "查看实例详情" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "没有条目显示。" @@ -171,16 +307,16 @@ #: static/horizon/js/horizon.tables.js:204 msgid "Please confirm your selection. " -msgstr "" +msgstr "请确认您的选择。" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" msgstr[0] "正在显示 %s 项" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "未授权不能进行此操作。" @@ -188,6 +324,89 @@ msgid "Passwords do not match." msgstr "密码不匹配。" +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "连接中" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "打开" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "关闭中" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "已关闭" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "状态: %s" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "是" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "不" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "找不到云硬盘快照。" + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "无法检索到镜像。" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "找不到镜像。" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" msgstr "" @@ -260,30 +479,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "无法获取网络" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "无法获取密钥对" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "无法获取可用域。" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "无法获取云主机类型列表。" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "无法获取安全组。" diff -Nru horizon-2015.1~b2/horizon/locale/zh_CN/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/zh_CN/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/zh_CN/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/zh_CN/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -4,14 +4,14 @@ # # Translators: # ChangBo Guo(gcb) , 2014 -# LIU Yulong , 2014 +# LIU Yulong , 2014-2015 # Xiao Xi LIU , 2014 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Chinese (China) (http://www.transifex.com/projects/p/horizon/language/zh_CN/)\n" "MIME-Version: 1.0\n" @@ -20,7 +20,7 @@ "Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "其它" @@ -38,12 +38,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "已经存在一个资源%(resource)s名称为\"%(name)s\"。" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "无权: %s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "未授权。请尝试重新登录。" @@ -81,7 +81,7 @@ msgstr "提交" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "取消" @@ -89,8 +89,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -98,7 +96,7 @@ #: tables/actions.py:645 msgid "This action cannot be undone." -msgstr "" +msgstr "这个动作不能撤消。" #: tables/actions.py:767 #, python-format @@ -295,7 +293,7 @@ #: templates/horizon/common/_limit_summary.html:29 #, python-format msgid "Allocated %(used)s of %(available)s " -msgstr "" +msgstr "已分配 %(used)s %(available)s " #: templates/horizon/common/_limit_summary.html:35 msgid "Security Groups" @@ -309,44 +307,7 @@ msgid "Volume Storage" msgstr "卷存储" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\n你可以通过从左边栏目目录中选取并移至右边栏目\n来指定元数据。你可以选择已添加至glance目录字典\n中的现成元数据,也可以使用\"其他\"选项来自定义\n元数据。" - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "可用的元数据" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "复制密钥是不允许的" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "没有可用的元数据" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "已存在的元数据" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\n你可以通过从左边栏目目录中选取并移至右边栏目\n来指定资源的元数据。在左边栏目中是来自\nGlance元数据目录的元数据定义。使用\"其他\"选项\n来增加你自定的元数据。" - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "保存" @@ -407,7 +368,7 @@ #: templates/horizon/common/_usage_summary.html:24 msgid "This Period's RAM-Hours:" -msgstr "" +msgstr "这一时期的内存-小时数" #: templates/horizon/common/_workflow.html:40 msgid "Back" @@ -421,26 +382,26 @@ msgid "Horizon" msgstr "控制面板" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "无限制" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "可用配额" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d 字节" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -450,17 +411,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0字节" @@ -476,26 +437,34 @@ msgid_plural "Sold Puppies" msgstr[0] "已售出的小狗" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "伪" + #: utils/filters.py:49 msgid "Never" msgstr "从不" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "不是一个有效的端口号" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "不是一个有效的IP协议号" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "用冒号分隔端口区间" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "端口号必须为整数" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "正在处理中, 请稍候..." diff -Nru horizon-2015.1~b2/horizon/locale/zh_TW/LC_MESSAGES/djangojs.po horizon-2015.1~b3/horizon/locale/zh_TW/LC_MESSAGES/djangojs.po --- horizon-2015.1~b2/horizon/locale/zh_TW/LC_MESSAGES/djangojs.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/zh_TW/LC_MESSAGES/djangojs.po 2015-03-19 19:07:29.000000000 +0000 @@ -3,13 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Chen, Shang-Pin , 2015 # Xiao Xi LIU , 2014 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-04 17:16-0600\n" -"PO-Revision-Date: 2015-02-04 21:29+0000\n" +"POT-Creation-Date: 2015-03-18 20:07-0500\n" +"PO-Revision-Date: 2015-03-18 23:58+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Chinese (Taiwan) (http://www.transifex.com/projects/p/horizon/language/zh_TW/)\n" "MIME-Version: 1.0\n" @@ -18,8 +19,152 @@ "Language: zh_TW\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: static/horizon/js/horizon.accordion_nav.js:84 -#: static/horizon/js/horizon.modals.js:313 +#: static/angular/action-list/button-tooltip.js:15 +msgid "" +"The action cannot be performed. The contents of this row have errors or are " +"missing information." +msgstr "" + +#: static/angular/metadata-display/metadata-display.js:33 +msgid "Detail Information" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:35 +msgid "" +"You can specify resource metadata by moving items from the left column to " +"the right column. In the left columns there are metadata definitions from " +"the Glance Metadata Catalog. Use the \"Other\" option to add metadata with " +"the key of your choice." +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:36 +msgid "Min" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:37 +msgid "Max" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:38 +msgid "Min length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:39 +msgid "Max length" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:40 +msgid "Pattern mismatch" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:41 +msgid "Integer required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:42 +msgid "Decimal required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:43 +msgid "Required" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:44 +msgid "Duplicate keys are not allowed" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:45 +#: static/horizon/js/horizon.forms.js:184 +msgid "Filter" +msgstr "篩選" + +#: static/angular/metadata-tree/metadata-tree.js:46 +msgid "Available Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:47 +msgid "Existing Metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:48 +msgid "Custom" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:49 +msgid "No available metadata" +msgstr "" + +#: static/angular/metadata-tree/metadata-tree.js:50 +msgid "No existing metadata" +msgstr "" + +#: static/angular/modal/modal.js:83 +msgid "Submit" +msgstr "提交" + +#: static/angular/modal/modal.js:84 static/angular/wizard/wizard.js:7 +#: static/horizon/js/horizon.modals.js:33 +msgid "Cancel" +msgstr "取消" + +#: static/angular/transfer-table/transfer-table.js:39 +msgid "Allocated" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:40 +msgid "Available" +msgstr "可用" + +#: static/angular/transfer-table/transfer-table.js:41 +msgid "Select one" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:42 +msgid "Select an item from Available items below" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:43 +msgid "No available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:44 +msgid "Expand to see allocated items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:45 +msgid "Expand to see available items" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:46 +msgid "Click to show or hide" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:47 +msgid "Re-order items using drag and drop" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:48 +msgid "Click to see more details" +msgstr "" + +#: static/angular/transfer-table/transfer-table.js:100 +msgid "Found %(found)s of %(total)s" +msgstr "" + +#: static/angular/wizard/wizard.js:8 +msgid "Back" +msgstr "上一頁" + +#: static/angular/wizard/wizard.js:9 +msgid "Next" +msgstr "下一步" + +#: static/angular/wizard/wizard.js:10 +msgid "Finish" +msgstr "完成" + +#: static/horizon/js/horizon.accordion_nav.js:78 +#: static/horizon/js/horizon.modals.js:315 #: static/horizon/js/horizon.tabs.js:21 msgid "Loading" msgstr "讀取中" @@ -30,9 +175,9 @@ msgstr "無可用的資料。" #: static/horizon/js/horizon.d3linechart.js:410 -#: static/horizon/js/horizon.modals.js:332 +#: static/horizon/js/horizon.modals.js:334 #: static/horizon/js/horizon.tables_inline_edit.js:94 -#: static/horizon/js/horizon.tables_inline_edit.js:158 +#: static/horizon/js/horizon.tables_inline_edit.js:157 msgid "An error occurred. Please try again later." msgstr "發生錯誤。請稍後再試。" @@ -41,16 +186,12 @@ msgid "There was a problem communicating with the server, please try again." msgstr "與伺服器間的通訊有問題,請再試一次。" -#: static/horizon/js/horizon.forms.js:184 -msgid "Filter" -msgstr "篩選" - -#: static/horizon/js/horizon.instances.js:272 +#: static/horizon/js/horizon.instances.js:273 msgid "Could not read the file" msgstr "不能讀取檔案" -#: static/horizon/js/horizon.instances.js:278 -#: static/horizon/js/horizon.instances.js:307 +#: static/horizon/js/horizon.instances.js:279 +#: static/horizon/js/horizon.instances.js:308 msgid "Could not decrypt the password" msgstr "不能解鎖密碼" @@ -82,16 +223,12 @@ msgid "Error: " msgstr "錯誤:" -#: static/horizon/js/horizon.modals.js:33 -msgid "Cancel" -msgstr "取消" - -#: static/horizon/js/horizon.modals.js:227 +#: static/horizon/js/horizon.modals.js:229 #: static/horizon/js/horizon.tables.js:218 msgid "Working" msgstr "運作中" -#: static/horizon/js/horizon.modals.js:261 +#: static/horizon/js/horizon.modals.js:263 msgid "There was an error submitting the form. Please try again." msgstr "提交表單時有錯誤。請再試一次。" @@ -122,7 +259,7 @@ #: static/horizon/js/horizon.networktopology.js:556 msgid "Open Console" -msgstr "" +msgstr "開啟 Console" #: static/horizon/js/horizon.networktopology.js:557 msgid "View Details" @@ -130,11 +267,11 @@ #: static/horizon/js/horizon.networktopology.js:560 msgid "Delete Router" -msgstr "" +msgstr "刪除路由器" #: static/horizon/js/horizon.networktopology.js:561 msgid "View Router Details" -msgstr "" +msgstr "檢視路由器詳細資訊" #: static/horizon/js/horizon.networktopology.js:564 msgid "Add Interface" @@ -142,14 +279,14 @@ #: static/horizon/js/horizon.networktopology.js:570 msgid "Terminate Instance" -msgstr "" +msgstr "終止執行實例" #: static/horizon/js/horizon.networktopology.js:571 msgid "View Instance Details" -msgstr "" +msgstr "檢視執行實例詳細資訊" #: static/horizon/js/horizon.tables.js:39 -#: static/horizon/js/horizon.tables.js:374 +#: static/horizon/js/horizon.tables.js:382 msgid "No items to display." msgstr "沒有項目可以列出。" @@ -170,16 +307,16 @@ #: static/horizon/js/horizon.tables.js:204 msgid "Please confirm your selection. " -msgstr "" +msgstr "請確認您的選擇。" -#: static/horizon/js/horizon.tables.js:361 +#: static/horizon/js/horizon.tables.js:369 #, c-format msgid "Displaying %s item" msgid_plural "Displaying %s items" -msgstr[0] "" +msgstr[0] "正在顯示 %s 項" #: static/horizon/js/horizon.tables_inline_edit.js:88 -#: static/horizon/js/horizon.tables_inline_edit.js:152 +#: static/horizon/js/horizon.tables_inline_edit.js:151 msgid "Not authorized to do this operation." msgstr "沒有足夠的權限執行這個操作。" @@ -187,29 +324,112 @@ msgid "Passwords do not match." msgstr "密碼沒有相配。" +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Connecting" +msgstr "連線中" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Open" +msgstr "開啟" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closing" +msgstr "關閉中" + +#: static/horizon/js/angular/directives/serialConsole.js:23 +msgid "Closed" +msgstr "已關閉" + +#: static/horizon/js/angular/directives/serialConsole.js:85 +#, c-format +msgid "Status: %s" +msgstr "狀態:%s" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "Yes" +msgstr "是" + +#: static/horizon/js/angular/filters/filters.js:37 +msgid "No" +msgstr "否" + +#: static/horizon/js/angular/filters/filters.js:53 +#: static/horizon/js/angular/filters/filters.js:140 +msgid "GB" +msgstr "GB" + +#: static/horizon/js/angular/filters/filters.js:70 +#: static/horizon/js/angular/filters/filters.js:142 +msgid "MB" +msgstr "MB" + +#: static/horizon/js/angular/filters/filters.js:138 +msgid "TB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:144 +msgid "KB" +msgstr "" + +#: static/horizon/js/angular/filters/filters.js:146 +msgid "bytes" +msgstr "" + +#: static/horizon/js/angular/services/hz.api.cinder.js:47 +msgid "Unable to retrieve volumes." +msgstr "無法取回儲存空間。" + +#: static/horizon/js/angular/services/hz.api.cinder.js:74 +msgid "Unable to retrieve volume snapshots." +msgstr "無法取回儲存空間即時存檔。" + +#: static/horizon/js/angular/services/hz.api.config.js:42 +msgid "Unable to retrieve user configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.config.js:61 +msgid "Unable to retrieve admin configuration." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.glance.js:38 +msgid "Unable to retrieve image." +msgstr "無法取回映像檔。" + +#: static/horizon/js/angular/services/hz.api.glance.js:81 +msgid "Unable to retrieve images." +msgstr "無法取回映像檔。" + +#: static/horizon/js/angular/services/hz.api.glance.js:133 +msgid "Unable to retrieve namespaces." +msgstr "無法取回名稱空間。" + +#: static/horizon/js/angular/services/hz.api.glance.js:147 +msgid "Unable to retrieve namespace." +msgstr "無法取回名稱空間。" + #: static/horizon/js/angular/services/hz.api.keystone.js:24 msgid "Unable to retrieve users" -msgstr "" +msgstr "無法取回名稱使用者。" #: static/horizon/js/angular/services/hz.api.keystone.js:31 msgid "Unable to create the user." -msgstr "" +msgstr "無法新增使用者。" #: static/horizon/js/angular/services/hz.api.keystone.js:38 msgid "Unable to delete the users." -msgstr "" +msgstr "無法刪除使用者。" #: static/horizon/js/angular/services/hz.api.keystone.js:45 msgid "Unable to retrieve the user" -msgstr "" +msgstr "無法取回使用者。" #: static/horizon/js/angular/services/hz.api.keystone.js:53 msgid "Unable to edit the user." -msgstr "" +msgstr "無法編輯使用者。" #: static/horizon/js/angular/services/hz.api.keystone.js:60 msgid "Unable to delete the user." -msgstr "" +msgstr "無法刪除使用者。" #: static/horizon/js/angular/services/hz.api.keystone.js:68 msgid "Unable to retrieve role" @@ -259,30 +479,102 @@ msgid "Unable to delete the domain." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:156 +#: static/horizon/js/angular/services/hz.api.keystone.js:157 msgid "Unable to retrieve projects" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:163 +#: static/horizon/js/angular/services/hz.api.keystone.js:164 msgid "Unable to create the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:170 +#: static/horizon/js/angular/services/hz.api.keystone.js:171 msgid "Unable to delete the projects." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:177 +#: static/horizon/js/angular/services/hz.api.keystone.js:178 msgid "Unable to retrieve the project" msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:185 +#: static/horizon/js/angular/services/hz.api.keystone.js:186 msgid "Unable to edit the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:192 +#: static/horizon/js/angular/services/hz.api.keystone.js:193 msgid "Unable to delete the project." msgstr "" -#: static/horizon/js/angular/services/hz.api.keystone.js:200 +#: static/horizon/js/angular/services/hz.api.keystone.js:201 msgid "Unable to grant the role." msgstr "" + +#: static/horizon/js/angular/services/hz.api.keystone.js:215 +msgid "Unable to fetch the service catalog." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:39 +msgid "Unable to retrieve networks." +msgstr "無法取回網路。" + +#: static/horizon/js/angular/services/hz.api.neutron.js:88 +msgid "Unable to create the network." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:108 +msgid "Unable to retrieve subnets." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:172 +msgid "Unable to create the subnet." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.neutron.js:192 +msgid "Unable to retrieve ports." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:40 +msgid "Unable to retrieve keypairs." +msgstr "無法取回密鑰對。" + +#: static/horizon/js/angular/services/hz.api.nova.js:61 +msgid "Unable to create the keypair." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:79 +msgid "Unable to retrieve availability zones." +msgstr "無法取回可用區域。" + +#: static/horizon/js/angular/services/hz.api.nova.js:117 +msgid "Unable to retrieve limits." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:144 +msgid "Unable to create the server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:158 +msgid "Unable to retrieve server." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:188 +msgid "Unable to retrieve extensions." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:233 +msgid "Unable to retrieve flavors." +msgstr "無法取回虛擬硬體樣板。" + +#: static/horizon/js/angular/services/hz.api.nova.js:251 +msgid "Unable to retrieve flavor." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.nova.js:265 +msgid "Unable to retrieve flavor extra specs." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.policy.js:65 +msgid "Policy check failed." +msgstr "" + +#: static/horizon/js/angular/services/hz.api.security-group.js:64 +msgid "Unable to retrieve security groups." +msgstr "無法取回安全性群組。" diff -Nru horizon-2015.1~b2/horizon/locale/zh_TW/LC_MESSAGES/django.po horizon-2015.1~b3/horizon/locale/zh_TW/LC_MESSAGES/django.po --- horizon-2015.1~b2/horizon/locale/zh_TW/LC_MESSAGES/django.po 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/locale/zh_TW/LC_MESSAGES/django.po 2015-03-19 19:07:23.000000000 +0000 @@ -3,13 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Chen, Shang-Pin , 2015 # Xiao Xi LIU , 2014 msgid "" msgstr "" "Project-Id-Version: Horizon\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-02 15:13-0600\n" -"PO-Revision-Date: 2015-02-02 19:18+0000\n" +"POT-Creation-Date: 2015-03-16 22:31-0500\n" +"PO-Revision-Date: 2015-03-17 03:17+0000\n" "Last-Translator: openstackjenkins \n" "Language-Team: Chinese (Taiwan) (http://www.transifex.com/projects/p/horizon/language/zh_TW/)\n" "MIME-Version: 1.0\n" @@ -18,7 +19,7 @@ "Language: zh_TW\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: base.py:483 templates/horizon/common/_modal_form_update_metadata.html:38 +#: base.py:475 msgid "Other" msgstr "其他" @@ -36,12 +37,12 @@ msgid "A %(resource)s with the name \"%(name)s\" already exists." msgstr "一個名為「%(name)s」的 %(resource)s 已經存在。" -#: exceptions.py:230 +#: exceptions.py:235 #, python-format msgid "Unauthorized: %s" msgstr "未授權:%s" -#: exceptions.py:233 +#: exceptions.py:238 msgid "Unauthorized. Please try logging in again." msgstr "未授權。請試著重新登入。" @@ -79,7 +80,7 @@ msgstr "提交" #: forms/views.py:133 -#: templates/horizon/common/_modal_form_update_metadata.html:235 +#: templates/horizon/common/_modal_form_update_metadata.html:25 #: templates/horizon/common/_workflow.html:49 msgid "Cancel" msgstr "取消" @@ -87,8 +88,6 @@ #: tables/actions.py:460 #: templates/horizon/common/_data_table_table_actions.html:21 #: templates/horizon/common/_data_table_table_actions.html:33 -#: templates/horizon/common/_modal_form_update_metadata.html:28 -#: templates/horizon/common/_modal_form_update_metadata.html:97 #: templates/horizon/common/_workflow_step_update_members.html:14 #: templates/horizon/common/_workflow_step_update_members.html:23 msgid "Filter" @@ -96,7 +95,7 @@ #: tables/actions.py:645 msgid "This action cannot be undone." -msgstr "" +msgstr "這個動作將無法回復。" #: tables/actions.py:767 #, python-format @@ -293,7 +292,7 @@ #: templates/horizon/common/_limit_summary.html:29 #, python-format msgid "Allocated %(used)s of %(available)s " -msgstr "" +msgstr "已配置 %(available)s 中的 %(used)s " #: templates/horizon/common/_limit_summary.html:35 msgid "Security Groups" @@ -307,44 +306,7 @@ msgid "Volume Storage" msgstr "儲存空間" -#: templates/horizon/common/_modal_form_update_metadata.html:12 -msgid "" -"\n" -" You can specify metadata by adding items from the left column to\n" -" the right column. You may select the metadata added to glance\n" -" dictionary or you can use the \"Other\" option using a key of\n" -" your choice.\n" -" " -msgstr "\n 您可以從左欄加入項目到右欄來指定描述資料。\n 您可以將這個描述資料加入到 Glance 字典\n 或者也可以使用「其他」選項來使用您自選的鍵。\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:26 -msgid "Available Metadata" -msgstr "可用的描述資料" - -#: templates/horizon/common/_modal_form_update_metadata.html:56 -msgid "Duplicate keys are not allowed" -msgstr "不允許重覆的鍵" - -#: templates/horizon/common/_modal_form_update_metadata.html:84 -#: templates/horizon/common/_modal_form_update_metadata.html:196 -msgid "No available metadata" -msgstr "無可用的描述資料" - -#: templates/horizon/common/_modal_form_update_metadata.html:95 -msgid "Existing Metadata" -msgstr "存在的描述資料" - -#: templates/horizon/common/_modal_form_update_metadata.html:212 -msgid "" -"\n" -" You can specify resource metadata by moving items from the left\n" -" column to the right column. In the left columns there are metadata\n" -" definitions from the Glance Metadata Catalog. Use the \"Other\" option\n" -" to add metadata with the key of your choice.\n" -" " -msgstr "\n 您可以從左欄移動項目到右欄來指定資源的描述資料。\n 左欄有來自 Glance 描述資料類別的描述資料定義。\n 使用「其他」選項以使用您自選的鍵來加入描述資料。\n " - -#: templates/horizon/common/_modal_form_update_metadata.html:234 +#: templates/horizon/common/_modal_form_update_metadata.html:24 #: workflows/base.py:594 msgid "Save" msgstr "儲存" @@ -405,7 +367,7 @@ #: templates/horizon/common/_usage_summary.html:24 msgid "This Period's RAM-Hours:" -msgstr "" +msgstr "此時段的記憶體時數" #: templates/horizon/common/_workflow.html:40 msgid "Back" @@ -419,26 +381,26 @@ msgid "Horizon" msgstr "Horizon" -#: templatetags/horizon.py:133 templatetags/horizon.py:144 +#: templatetags/horizon.py:137 templatetags/horizon.py:148 msgid "No Limit" msgstr "不限制" -#: templatetags/horizon.py:136 templatetags/horizon.py:138 +#: templatetags/horizon.py:140 templatetags/horizon.py:142 msgid "Available" msgstr "可用" -#: templatetags/sizeformat.py:49 templatetags/sizeformat.py:54 +#: templatetags/sizeformat.py:51 templatetags/sizeformat.py:56 #, python-format msgid "%(size)d Byte" msgid_plural "%(size)d Bytes" msgstr[0] "%(size)d 位元組" -#: templatetags/sizeformat.py:57 +#: templatetags/sizeformat.py:59 #, python-format msgid "%s KB" msgstr "%s KB" -#: templatetags/sizeformat.py:60 +#: templatetags/sizeformat.py:61 #, python-format msgid "%s MB" msgstr "%s MB" @@ -448,17 +410,17 @@ msgid "%s GB" msgstr "%s GB" -#: templatetags/sizeformat.py:66 +#: templatetags/sizeformat.py:65 #, python-format msgid "%s TB" msgstr "%s TB" -#: templatetags/sizeformat.py:68 +#: templatetags/sizeformat.py:66 #, python-format msgid "%s PB" msgstr "%s PB" -#: templatetags/sizeformat.py:77 +#: templatetags/sizeformat.py:74 msgid "0 Bytes" msgstr "0 位元組" @@ -474,26 +436,34 @@ msgid_plural "Sold Puppies" msgstr[0] "已賣出小狗" +#: test/tests/views.py:59 +msgid "Fake" +msgstr "Fake" + #: utils/filters.py:49 msgid "Never" msgstr "永不" -#: utils/validators.py:23 utils/validators.py:47 +#: utils/validators.py:26 utils/validators.py:50 msgid "Not a valid port number" msgstr "不是一個有效的埠口號碼" -#: utils/validators.py:28 +#: utils/validators.py:31 msgid "Not a valid IP protocol number" msgstr "不是一個有效的 IP 協定號碼" -#: utils/validators.py:42 +#: utils/validators.py:45 msgid "One colon allowed in port range" msgstr "允許以冒號隔開埠口範圍" -#: utils/validators.py:49 +#: utils/validators.py:52 msgid "Port number must be integer" msgstr "埠口號碼必須是整數" +#: utils/validators.py:59 +msgid "The string may only contain ASCII printable characters." +msgstr "" + #: workflows/base.py:71 msgid "Processing..." msgstr "處理中…" diff -Nru horizon-2015.1~b2/horizon/static/angular/action-list/action.html horizon-2015.1~b3/horizon/static/angular/action-list/action.html --- horizon-2015.1~b2/horizon/static/angular/action-list/action.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/action-list/action.html 2015-03-19 19:07:29.000000000 +0000 @@ -0,0 +1,6 @@ + \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/action-list/action-list.js horizon-2015.1~b3/horizon/static/angular/action-list/action-list.js --- horizon-2015.1~b2/horizon/static/angular/action-list/action-list.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/action-list/action-list.js 2015-03-19 19:07:29.000000000 +0000 @@ -0,0 +1,206 @@ +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name hz.widget.action-list + * @description + * + * # hz.widget.action-list + * + * The `actionList` directive supports displaying a set of buttons + * in a Bootstrap button group or button dropdown (single or split). + * + * | Directives | + * |-----------------------------------------------------------------| + * | {@link hz.widget.action-list.directive:actionList `actionList`} | + * | {@link hz.widget.menu.directive:menu `menu`} | + * | {@link hz.widget.action.directive:action `action`} | + * + */ + angular.module('hz.widget.action-list', [ 'ui.bootstrap' ]) + + /** + * @ngdoc directive + * @name hz.widget.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 + * + * + * + * ``` + */ + .directive('actionList', [ 'basePath', function(path) { + return { + restrict: 'E', + link: function (scope, element, attrs, ctrl, transclude) { + element.addClass('btn-group'); + } + }; + } + ]) + + /** + * @ngdoc directive + * @name hz.widget.action-list.directive:menu + * @element + * @description + * The `menu` directive is the wrapper for the set of + * actions to be displayed in a dropdown menu in a + * Bootstrap button dropdown. Actions to be displayed + * should be declared within this directive element. + * + * See the `action` directive below for more information. + * + * @restrict E + * @example + * + * ``` + * + * + * Edit + * + * + * Delete + * + * + * ``` + */ + .directive('menu', [ 'basePath', function(path) { + return { + restrict: 'E', + templateUrl: path + 'action-list/menu.html', + transclude: true, + link: function(scope, element, attrs, ctrl, transclude) { + var menu = element.find('ul'); + + // Append menu items to menu + transclude(scope, function(clone) { + menu.append(clone); + }); + + // Don't include directive element since + // it will misalign component look + menu.unwrap(); + } + }; + }]) + + /** + * @ngdoc directive + * @name hz.widget.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: \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/action-list/split-button.html horizon-2015.1~b3/horizon/static/angular/action-list/split-button.html --- horizon-2015.1~b2/horizon/static/angular/action-list/split-button.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/action-list/split-button.html 2015-03-19 19:07:29.000000000 +0000 @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/action-list/warning-tooltip.html horizon-2015.1~b3/horizon/static/angular/action-list/warning-tooltip.html --- horizon-2015.1~b2/horizon/static/angular/action-list/warning-tooltip.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/action-list/warning-tooltip.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,4 @@ +

{$ ::message $}

+ \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/bind-scope/bind-scope.js horizon-2015.1~b3/horizon/static/angular/bind-scope/bind-scope.js --- horizon-2015.1~b2/horizon/static/angular/bind-scope/bind-scope.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/bind-scope/bind-scope.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,60 @@ +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name hz.framework.bind-scope + * @description + * + * # hz.framework.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 hz.framework.bind-scope.directive:bindScope `bindScope`} | + * + */ + angular.module('hz.framework.bind-scope', []) + + /** + * @ngdoc directive + * @name hz.framework.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 + * ``` + * + * + * + * + * + * ``` + */ + .directive('bindScope', function() { + return { + restrict: 'A', + link: function(scope, element, attrs, ctrl, transclude) { + if (transclude) { + transclude(scope, function(clone) { + var detailElt = element.find('[bind-scope-target]'); + if (detailElt.length) { + detailElt.append(clone); + } + }); + } + } + }; + }); + +})(); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/bind-scope/bind-scope.spec.js horizon-2015.1~b3/horizon/static/angular/bind-scope/bind-scope.spec.js --- horizon-2015.1~b2/horizon/static/angular/bind-scope/bind-scope.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/bind-scope/bind-scope.spec.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,64 @@ +(function() { + 'use strict'; + + describe('hz.framework.bind-scope module', function() { + it('should have been defined', function() { + expect(angular.module('hz.framework.bind-scope')).toBeDefined(); + }); + }); + + describe('bind-scope directive', function() { + + var $scope, $element; + + beforeEach(module('hz')); + beforeEach(module('hz.widgets')); + beforeEach(module('hz.framework.bind-scope')); + + beforeEach(module('hz.framework.bind-scope', function($compileProvider) { + $compileProvider.directive('testBindScope', function() { + return { + restrict: 'E', + scope: { + itemList: '=' + }, + transclude: true, + template: '
  • ' + + ' ' + + '
' + }; + }); + })); + + 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.$digest(); + })); + + 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'); + }); + + }); + +})(); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/charts/charts.js horizon-2015.1~b3/horizon/static/angular/charts/charts.js --- horizon-2015.1~b2/horizon/static/angular/charts/charts.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/charts/charts.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,63 @@ +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name hz.widget.charts + * @description + * + * # hz.widget.charts + * + * The `hz.widget.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 hz.widget.charts.constant:chartSettings `chartSettings`} | + * + * | Directives | + * |-----------------------------------------------------------------| + * | {@link hz.widget.charts.directive:pieChart `pieChart`} | + * + */ + angular.module('hz.widget.charts', []) + + /** + * @ngdoc parameters + * @name hz.widget.charts.constant:chartsettings + * @param {number} innerRadius Pie chart inner radius in pixels, default: 0 + * @param {number} outerRadius Pie chart outer radius in pixels, default: 35 + * @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('chartSettings', { + innerRadius: 0, + outerRadius: 35, + showTitle: true, + showLabel: true, + showLegend: true, + tooltipIcon: 'fa-square' + }) + + /** + * @ngdoc filter + * @name hz.widget.charts.filter:showKeyFilter + * @function Filter based on 'hideKey' value of each slice + * @returns {function} A filtered list of keys to show in legend + * + */ + .filter('showKeyFilter', function() { + return function(items) { + return items.filter(function (item) { + return !item.hideKey; + }); + }; + }); + +})(); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/charts/chart-tooltip.html horizon-2015.1~b3/horizon/static/angular/charts/chart-tooltip.html --- horizon-2015.1~b2/horizon/static/angular/charts/chart-tooltip.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/charts/chart-tooltip.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,9 @@ +
+ + {$ tooltip.label $} + {$ tooltip.value $} +
\ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/charts/chart-tooltip.js horizon-2015.1~b3/horizon/static/angular/charts/chart-tooltip.js --- horizon-2015.1~b2/horizon/static/angular/charts/chart-tooltip.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/charts/chart-tooltip.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,47 @@ +(function() { + 'use strict'; + + angular.module('hz.widget.charts') + + /** + * @ngdoc directive + * @name hz.widget.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 + * ``` + * + * ``` + * + */ + .directive('chartTooltip', [ 'basePath', function (path) { + return { + restrict: 'E', + scope: { + tooltip: '=tooltipData' + }, + templateUrl: path + 'charts/chart-tooltip.html' + }; + }]); + +})(); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/charts/chart-tooltip.scss horizon-2015.1~b3/horizon/static/angular/charts/chart-tooltip.scss --- horizon-2015.1~b2/horizon/static/angular/charts/chart-tooltip.scss 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/charts/chart-tooltip.scss 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,25 @@ +.chart-tooltip { + background-color: $tooltip-bg-color; + border: $tooltip-border; + box-shadow: $tooltip-box-shadow; + display: none; + padding: $tooltip-padding; + position: absolute; + white-space: nowrap; + z-index: 12000; + + &.tooltip-enabled { + display: inline-block; + } + + .tooltip-key { + color: $tooltip-key-color; + font-weight: $tooltip-key-weight; + padding: $tooltip-key-padding; + } + + i.fa { + background-color: inherit; + fill: none; + } +} \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/charts/pie-chart.html horizon-2015.1~b3/horizon/static/angular/charts/pie-chart.html --- horizon-2015.1~b2/horizon/static/angular/charts/pie-chart.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/charts/pie-chart.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,30 @@ +
+ + +
+ {$ ::chartData.title $} ({$ model.total $} Max) +
+ + + + + + {$ chartData.label $} + + + + +
+
+
+ {$ slice.value $} {$ slice.label $} +
+
+
\ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/charts/pie-chart.js horizon-2015.1~b3/horizon/static/angular/charts/pie-chart.js --- horizon-2015.1~b2/horizon/static/angular/charts/pie-chart.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/charts/pie-chart.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,165 @@ +(function() { + 'use strict'; + + angular.module('hz.widget.charts') + + /** + * @ngdoc directive + * @name hz.widget.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%', + * data: [ + * { label: 'Current', value: 1, color: '#1f83c6' }, + * { label: 'Added', value: 1, color: '#81c1e7' }, + * { label: 'Remaining', value: 6, colorClass: 'remaining', hideKey: true } + * ] + * }; + * + * title - the chart title + * label - the text to show in center of chart + * data - the data used to render chart + * + * var chartSettings = { + * innerRadius: 35, + * outerRadius: 50, + * showLabel: false + * }; + * ``` + * + * @restrict E + * @scope true + * + * @example + * ``` + * Pie Chart: + * + * + * Donut Chart: + * + * ``` + * + */ + .directive('pieChart', [ 'basePath', 'chartSettings', function (path, chartSettings) { + return { + restrict: 'E', + scope: { + chartData: '=', + chartSettings: '=' + }, + replace: true, + templateUrl: path + 'charts/pie-chart.html', + link: function (scope, element, attrs) { + var settings = angular.extend({}, chartSettings, scope.chartSettings); + settings.diameter = settings.outerRadius * 2; + + var model = { + settings: settings, + tooltipData: { + enabled: false, + icon: settings.tooltipIcon, + style: angular.extend({}, settings.tooltip) + } + }; + + 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 tooltip = d3Elt.select('chart-tooltip'); + + var unwatch = scope.$watch('chartData', updateChart); + scope.$on('$destroy', unwatch); + + scope.model = model; + + function updateChart() { + scope.model.total = d3.sum(scope.chartData.data, function(d) { return d.value; }); + scope.model.tooltipData.enabled = false; + + // Generate or update slices + 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 eltHeight = element[0].getBoundingClientRect().height; + var titleHeight = element[0].querySelector('div.pie-chart-title') + .getBoundingClientRect() + .height; + + var point = d3.mouse(elt); + var x = point[0] + scope.model.settings.outerRadius; + var y = eltHeight - point[1] - scope.model.settings.outerRadius - titleHeight; + + scope.model.tooltipData.label = d.data.label; + scope.model.tooltipData.value = d.data.value; + scope.model.tooltipData.enabled = true; + scope.model.tooltipData.iconColor = d.data.color; + scope.model.tooltipData.iconClass = d.data.colorClass; + scope.model.tooltipData.style.left = x + 'px'; + scope.model.tooltipData.style.bottom = y + 'px'; + }); + } + + function clearTooltip() { + scope.$apply(function() { + scope.model.tooltipData.enabled = false; + }); + } + } + }; + }]); + +})(); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/charts/pie-chart.scss horizon-2015.1~b3/horizon/static/angular/charts/pie-chart.scss --- horizon-2015.1~b2/horizon/static/angular/charts/pie-chart.scss 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/charts/pie-chart.scss 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,44 @@ +.pie-chart { + display: inline-block; + position: relative; + + .svg-pie-chart { + float: left; + + .slice { + cursor: pointer; + } + } + + .pie-chart-title { + font-size: $chart-title-font-size; + font-weight: $chart-title-weight; + padding: $chart-title-padding; + } + + .pie-chart-label { + font-size: 1.2em; + text-anchor: middle; + } + + .pie-chart-legend { + float: left; + font-size: $chart-legend-font-size; + line-height: 1em; + padding: $chart-legend-padding; + + .slice-legend { + padding: 0.1em 0; + + .slice-key { + color: transparent; + display: inline-block; + height: 1em; + line-height: 1em; + position: relative; + top: 0.12em; + width: 0.5em; + } + } + } +} \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/charts/pie-chart.spec.js horizon-2015.1~b3/horizon/static/angular/charts/pie-chart.spec.js --- horizon-2015.1~b2/horizon/static/angular/charts/pie-chart.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/charts/pie-chart.spec.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,88 @@ +/* jshint globalstrict: true */ +'use strict'; + +describe('hz.widget.charts module', function () { + it('should be defined', function () { + expect(angular.module('hz.widget.charts')).toBeDefined(); + }); +}); + +describe('pie chart directive', function() { + + var $scope, $element; + + beforeEach(module('templates')); + beforeEach(module('hz')); + beforeEach(module('hz.widgets')); + beforeEach(module('hz.widget.charts')); + + beforeEach(inject(function($injector) { + var $compile = $injector.get('$compile'); + $scope = $injector.get('$rootScope').$new(); + + $scope.testData = { + title: 'Total Instances', + label: '25%', + data: [ + { label: 'Current', value: 1, color: '#1f83c6' }, + { label: 'Added', value: 1, color: '#81c1e7' }, + { label: 'Remaining', value: 6, color: '#d1d3d4', hideKey: true } + ] + }; + + var settings = '{ "innerRadius": 25 }'; + var markup = ""; + $element = angular.element(markup); + $compile($element)($scope); + + $scope.$digest(); + })); + + it('should be compiled', function() { + expect($element.html().trim()).not.toBe(''); + }); + + it('should have svg element', function() { + expect($element.find('svg')).toBeDefined(); + }); + + it('should have 3 path elements', function() { + expect($element.find('path.slice').length).toBe(3); + }); + + it('should have correct colors for slices', function() { + var slices = $element.find('path.slice'); + + var slice1Color = slices[0].style.fill; + + if (slice1Color.indexOf('rgb') === 0) { + expect(slices[0].style.fill).toBe('rgb(31, 131, 198)'); + expect(slices[1].style.fill).toBe('rgb(129, 193, 231)'); + expect(slices[2].style.fill).toBe('rgb(209, 211, 212)'); + } else { + expect(slices[0].style.fill).toBe('#1f83c6'); + expect(slices[1].style.fill).toBe('#81c1e7'); + expect(slices[2].style.fill).toBe('#d1d3d4'); + } + }); + + it('should have a correct title "Total Instances (8 Max)"', function() { + var title = $element.find('.pie-chart-title').text().trim(); + expect(title).toBe('Total Instances (8 Max)'); + }); + + it('should have a legend', function() { + expect($element.find('.pie-chart-legend')).toBeDefined(); + }); + + it ('should have correct legend keys and labels', function() { + var legendKeys = $element.find('.pie-chart-legend .slice-legend'); + + var firstKeyLabel = legendKeys[0]; + var secondKeyLabel = legendKeys[1]; + + expect(firstKeyLabel.textContent.trim()).toBe('1 Current'); + expect(secondKeyLabel.textContent.trim()).toBe('1 Added'); + }); + +}); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/form/form.js horizon-2015.1~b3/horizon/static/angular/form/form.js --- horizon-2015.1~b2/horizon/static/angular/form/form.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/form/form.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,71 @@ +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name hz.widget.form + * + * # hz.widget.form + * + * The `hz.widget.form` provides form directives and services. + * + * | Components | + * |----------------------------------------------------------| + * | {@link hz.widget.form.hzPasswordMatch `hzPasswordMatch`} | + * + */ + var app = angular.module('hz.widget.form', []); + + /** + * @ngdoc directive + * @name hzPasswordMatch + * + * @description + * A directive to ensure that password matches. + * Changing the password or confirmation password triggers a validation check. + * However, only the confirmation password will show an error if match is false. + * The goal is to check that confirmation password matches the password, + * not whether the password matches the confirmation password. + * The behavior here is NOT bi-directional. + * + * @requires + * ng-model - model for confirmation password + * + * @scope + * hzPasswordMatch - form model 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. + */ + app.directive('hzPasswordMatch', function(){ + return { + restrict: 'A', + require: 'ngModel', + scope: { pw: '=hzPasswordMatch' }, + link: function(scope, element, attr, ctrl){ + + // helper function to check that password matches + function passwordCheck(){ + scope.$apply(function(){ + var match = (ctrl.$modelValue === scope.pw.$modelValue); + ctrl.$setValidity('match', match); + }); + } + + // this ensures that typing in either input + // will trigger the password match + var pwElement = $('#'+scope.pw.$name); + pwElement.on('keyup change', passwordCheck); + element.on('keyup change', passwordCheck); + + } // end of link + }; // end of return + }); // end of directive + +})(); diff -Nru horizon-2015.1~b2/horizon/static/angular/form/form.spec.js horizon-2015.1~b3/horizon/static/angular/form/form.spec.js --- horizon-2015.1~b2/horizon/static/angular/form/form.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/form/form.spec.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,93 @@ +/* jshint globalstrict: true */ +'use strict'; + +describe('hz.widget.form module', function(){ + it('should have been defined', function(){ + expect(angular.module('hz.widget.form')).toBeDefined(); + }); +}); + +describe('form directives', function() { + beforeEach(module('hz')); + beforeEach(module('hz.widgets')); + beforeEach(module('hz.widget.form')); + + describe('hzPasswordMatch directive', function() { + + var $compile, $rootScope; + var element, password, cpassword; + var markup = + '
' + + '' + + '' + + '
'; + + 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.$digest(); + })); + + 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(done) { + $rootScope.user.password = 'password'; + $rootScope.$digest(); + cpassword.change(); + setTimeout(function(){ + expect(cpassword.val()).not.toEqual(password.val()); + expect(cpassword.hasClass('ng-invalid')).toBe(true); + done(); + }, 1000); + }); + + it('should not match if user changes only confirmation password', function(done) { + $rootScope.user.cpassword = 'password'; + $rootScope.$digest(); + cpassword.change(); + setTimeout(function(){ + expect(cpassword.val()).not.toEqual(password.val()); + expect(cpassword.hasClass('ng-invalid')).toBe(true); + done(); + }, 1000); + }); + + it('should match if both passwords are the same', function(done) { + $rootScope.user.password = 'password'; + $rootScope.user.cpassword = 'password'; + $rootScope.$digest(); + cpassword.change(); + setTimeout(function(){ + expect(cpassword.val()).toEqual(password.val()); + expect(cpassword.hasClass('ng-valid')).toBe(true); + done(); + }, 1000); + }); + + it('should not match if both passwords are different', function(done) { + $rootScope.user.password = 'password123'; + $rootScope.user.cpassword = 'password345'; + $rootScope.$digest(); + cpassword.change(); + setTimeout(function(){ + expect(cpassword.val()).not.toEqual(password.val()); + expect(cpassword.hasClass('ng-invalid')).toBe(true); + done(); + }, 1000); + }); + + }); // end of hzPasswordMatch directive +}); // end of form directives diff -Nru horizon-2015.1~b2/horizon/static/angular/help-panel/help-panel.scss horizon-2015.1~b3/horizon/static/angular/help-panel/help-panel.scss --- horizon-2015.1~b2/horizon/static/angular/help-panel/help-panel.scss 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/help-panel/help-panel.scss 2015-03-19 19:07:23.000000000 +0000 @@ -33,6 +33,10 @@ &.open { right: 0; + border: 1px solid $helpPanelBorderColor; + border-right: none; + margin-top: -1px; + margin-bottom: -1px; & > button.open { display: none; @@ -56,14 +60,18 @@ border: none; text-align: center; vertical-align: middle; - background: $helpPanelBg; + background: $helpPanelBtnBg; + border: 1px solid $helpPanelBorderColor; + border-right: none; + margin-top: -1px; + // button icon & > * { display: inline-block; vertical-align: middle; - background: $helpPanelBtnBg; - color: $helpPanelBtnColor; + background: $helpPanelBtnIconBg; + color: $helpPanelBtnIconColor; font-size: $helpPanelBtnIconSize; } diff -Nru horizon-2015.1~b2/horizon/static/angular/help-panel/help-panel.spec.js horizon-2015.1~b3/horizon/static/angular/help-panel/help-panel.spec.js --- horizon-2015.1~b2/horizon/static/angular/help-panel/help-panel.spec.js 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/help-panel/help-panel.spec.js 2015-03-19 19:07:23.000000000 +0000 @@ -1,3 +1,4 @@ +/* jshint globalstrict: true */ 'use strict'; describe('hz.widget.help-panel module', function() { diff -Nru horizon-2015.1~b2/horizon/static/angular/metadata-display/metadata-display.html horizon-2015.1~b3/horizon/static/angular/metadata-display/metadata-display.html --- horizon-2015.1~b2/horizon/static/angular/metadata-display/metadata-display.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/metadata-display/metadata-display.html 2015-03-19 19:07:29.000000000 +0000 @@ -0,0 +1,33 @@ + \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/metadata-display/metadata-display.js horizon-2015.1~b3/horizon/static/angular/metadata-display/metadata-display.js --- horizon-2015.1~b2/horizon/static/angular/metadata-display/metadata-display.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/metadata-display/metadata-display.js 2015-03-19 19:07:29.000000000 +0000 @@ -0,0 +1,122 @@ +(function () { + 'use strict'; + + /** + * @ngdoc overview + * @name hz.widget.metadata-display + * @description + * + * # hz.widget.metadata-display + * + * The `hz.widget.metadata-display` provides widget displaying metadata. + * + * | Directives | + * |---------------------------------------------------------------------------------------------| + * | {@link hz.widget.metadata-display.directive:hzMetadataDisplay `hzMetadataDisplay`} | + * |---------------------------------------------------------------------------------------------| + * | Controllers | + * |---------------------------------------------------------------------------------------------| + * | {@link hz.widget.metadata-display.controller:hzMetadataDisplayCtrl `hzMetadataDisplayCtrl`} | + * + */ + angular.module('hz.widget.metadata-display', [ + 'hz.widget.metadata-tree' + ]) + + /** + * @ngdoc parameters + * @name hz.widget.metadata-display:metadataTreeDefaults + * @param {object} text Text constants + */ + .constant('metadataDisplayDefaults', { + text: { + detail: gettext('Detail Information') + } + }) + + /** + * @ngdoc directive + * @name hz.widget.metadata-display.directive:hzMetadataDisplay + * @scope + * + * @description + * The `hzMetadataDisplay` displays existing metadata. + * + * @param {object[]} available List of available namespaces + * @param {object} existing Key-value pairs with existing properties + * @param {object=} text Text override + */ + .directive('hzMetadataDisplay', ['basePath', + function (path) { + return { + scope: { + available: '=', + existing: '=', + text: '=?' + }, + controller: 'hzMetadataDisplayCtrl', + templateUrl: path + 'metadata-display/metadata-display.html' + }; + } + ]) + + /** + * @ngdoc controller + * @name hz.widget.metadata-display.controller:hzMetadataDisplayCtrl + * @description + * Controller used by `hzMetadataDisplay` + */ + .controller('hzMetadataDisplayCtrl', [ + '$scope', 'metadataTreeService', 'metadataDisplayDefaults', + function ($scope, metadataTreeService, defaults) { + + function init() { + $scope.tree = new metadataTreeService.Tree($scope.available, $scope.existing); + angular.forEach($scope.tree.flatTree, function (item) { + if(item.added) { + if(!item.leaf) { + item.added = false; + if (item.parent) { + item.parent.addedCount -= 1; + } + } + else if(!item.custom) { + $scope.hide = false; + } + } + + }); + // select first item + $scope.tree.tree.some(function (item) { + if($scope.listFilter(item)) { + $scope.selected = item; + item.expand(true); + return true; // break + } + }); + } + + $scope.onSelect = function (item) { + $scope.selected.collapse(); + item.expand(true); + $scope.selected = item; + }; + + $scope.childrenFilter = function (item) { + return item.visible && item.leaf && item.added; + }; + + $scope.listFilter = function (item) { + return item.addedCount > 0; + }; + + $scope.text = angular.extend({}, defaults.text, $scope.text); + $scope.tree = null; + $scope.selected = null; + $scope.hide = true; + + init(); + } + ]); + +}()); diff -Nru horizon-2015.1~b2/horizon/static/angular/metadata-display/metadata-display.scss horizon-2015.1~b3/horizon/static/angular/metadata-display/metadata-display.scss --- horizon-2015.1~b2/horizon/static/angular/metadata-display/metadata-display.scss 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/metadata-display/metadata-display.scss 2015-03-19 19:07:29.000000000 +0000 @@ -0,0 +1,29 @@ +.metadata-display { + .selector { + .selector-item { + border-top: 1px solid $metadata-display-separator-color; + padding: 10px; + color: $metadata-display-selector-color; + + &:first-child { + border-top: none; + } + + &:hover { + color: $metadata-display-selector-hover-color; + } + + &.active { + color: $metadata-display-selector-active-color; + } + } + } + + .description { + margin-top: 20px; + } + + .auto-width { + width: auto; + } +} \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/metadata-display/metadata-display.spec.js horizon-2015.1~b3/horizon/static/angular/metadata-display/metadata-display.spec.js --- horizon-2015.1~b2/horizon/static/angular/metadata-display/metadata-display.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/metadata-display/metadata-display.spec.js 2015-03-19 19:07:29.000000000 +0000 @@ -0,0 +1,135 @@ +/* jshint globalstrict: true */ +'use strict'; + +describe('hz.widget.metadata-display module', function() { + it('should have been defined', function () { + expect(angular.module('hz.widget.metadata-display')).toBeDefined(); + }); + + var namespaces = [ + { + "display_name": "Test Namespace A", + "description": "Test namespace description", + "properties": { + "test:A:1": { + "title": "Test A.1 - string", + "type": "string", + "default": "foo", + "enum": [ + "option-1", "option-2", "option-3" + ] + }, + "test:A:2": { + "title": "Test A.2 - integer", + "type": "integer", + "default": "1", + "minimum": 0, + "maximum": 10 + }, + "test:A:3": { + "title": "Test A.3 - number", + "type": "number", + "default": "1.1", + "minimum": 0, + "maximum": 10 + }, + "test:A:4": { + "title": "Test A.4 - boolean", + "type": "boolean", + "default": "True" + }, + "test:A:5": { + "title": "Test A.5 - boolean", + "type": "boolean", + "default": "false" + }, + "test:A:6": { + "title": "Test A.6 - array", + "type": "array", + "items": { + "type": "string", + "enum": [ + "val-1", "val-2", "val-3" + ] + } + } + } + }, + { + "display_name": "Test Namespace B", + "description": "Test namespace description", + "objects": [ + { + "name": "Test Object A", + "description": "Test object description", + "properties": { + "test:B:A:1": { + "title": "Test B.A.1", + "description": "Test description" + }, + "test:B:A:2": {} + } + }, + { + "name": "Test Object B", + "description": "Test object description", + "properties": { + "test:B:B:1": {}, + "test:B:B:2": {} + } + } + ] + } + ]; + + var existing = { + 'test:A:1': 'option-2', + 'test:A:2': '5', + 'test:B:A:1': 'foo', + 'test:B:B:1': 'bar' + }; + + describe('hzMetadataDisplay directive', function () { + var $scope, $element; + + beforeEach(module('templates')); + beforeEach(module('hz')); + beforeEach(module('hz.widgets')); + beforeEach(module('hz.widget.metadata-tree')); + beforeEach(module('hz.widget.metadata-display')); + beforeEach(inject(function ($injector) { + var $compile = $injector.get('$compile'); + $scope = $injector.get('$rootScope').$new(); + + $scope.available = namespaces; + $scope.existing = existing; + + var markup = + ''; + + $element = angular.element(markup); + $compile($element)($scope); + $scope.$digest(); + })); + + it('should have 3 rows in selector list', function() { + expect($element.find('.selector .selector-item').length).toBe(3); + }); + + it('should have 2 items in first group', function() { + expect($element.find('div[ng-repeat] div.auto-width').length).toBe(2); + }); + + it('should have 1 item in second group', function() { + $element.find('.selector .selector-item:nth-child(2)').trigger('click'); + expect($element.find('div[ng-repeat] div.auto-width').length).toBe(1); + }); + + it('should have proper description', function() { + expect($element.find('span[ng-bind="selected.description"]').text()).toBe(namespaces[0].description); + $element.find('.selector .selector-item:nth-child(2)').trigger('click'); + expect($element.find('span[ng-bind="selected.description"]').text()).toBe(namespaces[1].objects[0].description); + }); + }); + +}); diff -Nru horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree.html horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree.html --- horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,134 @@ + diff -Nru horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree-item.html horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree-item.html --- horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree-item.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree-item.html 2015-03-19 19:07:29.000000000 +0000 @@ -0,0 +1,78 @@ + \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree.js horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree.js --- horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,189 @@ +(function () { + 'use strict'; + + /** + * @ngdoc overview + * @name hz.widget.metadata-tree + * @description + * + * # hz.widget.metadata-tree + * + * The `hz.widget.metadata-tree` provides widgets and service + * with logic for editing metadata. + * + * | Directives | + * |--------------------------------------------------------------------------------------------| + * | {@link hz.widget.metadata-tree.directive:hzMetadataTree `hzMetadataTree`} | + * | {@link hz.widget.metadata-tree.directive:hzMetadataTreeItem `hzMetadataTreeItem`} | + * | {@link hz.widget.metadata-tree.directive:hzMetadataTreeUnique `hzMetadataTreeUnique`} | + * |--------------------------------------------------------------------------------------------| + * | Controllers | + * |--------------------------------------------------------------------------------------------| + * | {@link hz.widget.metadata-tree.controller:hzMetadataTreeCtrl `hzMetadataTreeCtrl`} | + * | {@link hz.widget.metadata-tree.controller:hzMetadataTreeItemCtrl `hzMetadataTreeItemCtrl`} | + * + */ + angular.module('hz.widget.metadata-tree', []) + + /** + * @ngdoc parameters + * @name hz.widget.metadata-tree.constant:metadataTreeDefaults + * @param {object} text Text constants + */ + .constant('metadataTreeDefaults', { + text: { + help: gettext('You can specify resource metadata by moving items from the left column to the right column. In the left columns there are metadata definitions from the Glance Metadata Catalog. Use the "Other" option to add metadata with the key of your choice.'), + min: gettext('Min'), + max: gettext('Max'), + minLength: gettext('Min length'), + maxLength: gettext('Max length'), + patternMismatch: gettext('Pattern mismatch'), + integerRequired: gettext('Integer required'), + decimalRequired: gettext('Decimal required'), + required: gettext('Required'), + duplicate: gettext('Duplicate keys are not allowed'), + filter: gettext('Filter'), + available: gettext('Available Metadata'), + existing: gettext('Existing Metadata'), + custom: gettext('Custom'), + noAvailable: gettext('No available metadata'), + noExisting: gettext('No existing metadata') + } + }) + + /** + * @ngdoc directive + * @name hz.widget.metadata-tree.directive:hzMetadataTree + * @scope + * + * @description + * The `hzMetadataTree` directive provide support for modifying existing + * metadata properties and adding new from metadata catalog. + * + * @param {Tree=} model Model binding + * @param {object[]=} available List of available namespaces + * @param {object=} existing Key-value pairs with existing properties + * @param {object=} text Text override + */ + .directive('hzMetadataTree', ['basePath', + function (path) { + return { + scope: { + tree: '=*?model', + available: '=?', + existing: '=?', + text: '=?' + }, + controller: 'hzMetadataTreeCtrl', + templateUrl: path + 'metadata-tree/metadata-tree.html' + }; + } + ]) + + /** + * @ngdoc directive + * @name hz.widget.metadata-tree.directive:hzMetadataTreeItem + * @scope + * + * @description + * The `hzMetadataTreeItem` helper directive displays proper field for + * editing Item.leaf.value depending on Item.leaf.type + * + * @param {expression} action Action for button + * @param {Item} item Item to display + * @param {object} text Text override + */ + .directive('hzMetadataTreeItem', ['basePath', + function (path) { + return { + scope: { + action: '&', + item: '=', + text: '=' + }, + controller: 'hzMetadataTreeItemCtrl', + templateUrl: path + 'metadata-tree/metadata-tree-item.html' + }; + } + ]) + + /** + * @ngdoc directive + * @name hz.widget.metadata-tree.directive:hzMetadataTreeUnique + * @restrict A + * + * @description + * The `hzMetadataTreeUnique` helper directive provides validation + * for field which value should be unique new Item + */ + .directive('hzMetadataTreeUnique', function () { + return { + restrict: 'A', + require: 'ngModel', + link: function (scope, elm, attrs, ctrl) { + ctrl.$validators.unique = function(modelValue, viewValue) { + return !scope.tree.flatTree.some(function (item) { + return item.leaf && item.leaf.name === viewValue; + }); + }; + } + }; + }) + + /** + * @ngdoc controller + * @name hz.widget.metadata-tree.controller:hzMetadataTreeCtrl + * @description + * Controller used by `hzMetadataTree` + */ + .controller('hzMetadataTreeCtrl', [ + '$scope', 'metadataTreeService', 'metadataTreeDefaults', + function ($scope, metadataTreeService, defaults) { + + $scope.availableFilter = function (item) { + return !item.added && ( + $scope.filterText.available.length === 0 ? item.visible : true); + }; + + $scope.text = angular.extend({}, defaults.text, $scope.text); + if(!$scope.tree) { + $scope.tree = new metadataTreeService.Tree($scope.available, $scope.existing); + } + $scope.customItem = ''; + $scope.filterText = { + available: '', + existing: '' + }; + } + ]) + + /** + * @ngdoc controller + * @name hz.widget.metadata-tree.controller:hzMetadataTreeItemCtrl + * @description + * Controller used by `hzMetadataTreeItem` + */ + .controller('hzMetadataTreeItemCtrl', [ + '$scope', + function ($scope) { + $scope.formatErrorMessage = function (item, error) { + if(error.min) return $scope.text.min + ' ' + item.leaf.minimum; + if(error.max) return $scope.text.max + ' ' + item.leaf.maximum; + if(error.minlength) return $scope.text.minLength + ' ' + item.leaf.minLength; + if(error.maxlength) return $scope.text.maxLength + ' ' + item.leaf.maxLength; + if(error.pattern) { + if(item.leaf.type === 'integer') return $scope.text.integerRequired; + else return $scope.text.patternMismatch; + } + if(error.number) { + if(item.leaf.type === 'integer') return $scope.text.integerRequired; + else return $scope.text.decimalRequired; + } + if(error.required) { + return $scope.text.required; + } + }; + } + ]); + +}()); diff -Nru horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree.scss horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree.scss --- horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree.scss 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree.scss 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,91 @@ +.metadata-tree { + min-height: 200px; + + .panel .list-group { + height: 400px; + overflow: auto; + } + + /* Header */ + + .panel-heading { + .v-align { + display: table; + min-height: 2.5em; + } + + .v-align > * { + display: table-cell; + vertical-align: middle; + } + } + + /* Item lists */ + + :not(.active) { + &.dark-stripe { + background-color: $table-bg-odd; + } + &.light-stripe { + background-color: white; + } + } + + .list-group-item { + &.level-0>* { + padding-left: 0; + } + + &.level-1>* { + padding-left: 15px; + } + + &.level-2>* { + padding-left: 30px; + } + + .leaf { + padding-left: 10px; + } + } + + .metadata-tree-item { + display: block; + + .input-group-addon { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 50%; + max-width: 140px; + text-align: right; + } + + .label-info { + display: none; + position: absolute; + z-index: 10; + top: 1px; + left: 25px; + max-width: 80%; + overflow: hidden; + text-overflow: ellipsis; + } + + .bool { + width: 40%; + } + } + + .label-danger { + display: inline-block; + position: absolute; + z-index: 10; + bottom: 1px; + left: 25px; + } + + .list-group-item:hover .label-info { + display: inline-block; + } +} diff -Nru horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree-service.js horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree-service.js --- horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree-service.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree-service.js 2015-03-19 19:07:29.000000000 +0000 @@ -0,0 +1,489 @@ +(function () { + 'use strict'; + + angular.module('hz.widget.metadata-tree') + + /** + * @ngdoc service + * @name hz.widget.metadata-tree.metadataTreeService + */ + .factory('metadataTreeService', [function () { + + /** + * Parse value into boolean + * + * @param {(string|boolean)} value + * @returns {boolean} + */ + function parseBool(value) { + var value_type = typeof(value); + + if(value_type === 'boolean') { + return value; + } + else if(value_type === 'string') { + value = value.toLowerCase(); + + if(value === 'true') { + return true; + } + else if(value === 'false') { + return false; + } + } + + return null; + } + + /** + * Construct a new property + * + * @class Property + * @param {string} name + * @param {Object} [json] + * + * @property {string} name Property key name + * @property {string} title Property display name + * @property {string} description Property description + * @property {*} value Property value + * @property {string} default Property default value + * @property {string} type Property type + * @property {boolean} readonly Property readonly state + */ + function Property(name, json) { + this.name = name; + this.title = name; + this.description = ''; + this.value = null; + this.default = null; + this.type = 'string'; + this.readonly = false; + angular.extend(this, json); + this.setValue(this.default); + } + + /** + * Deserialize value and assign it to {@link Property#value} + * + * @param {string} value + */ + Property.prototype.setValue = function(value) { + if(value === null) { + this.value = null; + return; + } + + switch (this.type) { + case 'integer': this.value = parseInt(value); break; + case 'number': this.value = parseFloat(value); break; + case 'array': this.value = value.replace(/^ /, ''); break; + case 'boolean': this.value = parseBool(value); break; + default: this.value = value; + } + }; + + /** + * Serialize {@link Property#value} and returns it + * + * @returns {*} + */ + Property.prototype.getValue = function() { + switch (this.type) { + case 'array': return ' ' + this.value; + default: return this.value; + } + }; + + /** + * Construct a new tree node + * + * @class Item + * @param {Item} parent + * + * @property {Item} parent Item parent + * @property {Item[]} children Item children + * @property {boolean} visible Item visibility + */ + function Item(parent) { + // parent as property to prevent infinite recursion in angular filter + Object.defineProperty(this, 'parent', { + value: typeof parent !== 'undefined' ? parent : null + }); + this.children = []; + // Node properties + this.visible = false; + this.expanded = false; + this.label = ''; + this.description = ''; + this.level = parent ? parent.level + 1 : 0; + this.addedCount = 0; + this.custom = false; + // Leaf properties + this.leaf = null; + this.added = false; + } + + /** + * Load Item values and child Items from namespace definition + * + * @param {object} namespace Metadata namespace definition + * @returns {Item} + */ + Item.prototype.fromNamespace = function (namespace) { + this.label = namespace.display_name; + this.description = namespace.description; + + if(namespace.objects) { + angular.forEach(namespace.objects, function (object) { + this.children.push(new Item(this).fromObject(object)); + }, this); + } + + if(namespace.properties) { + angular.forEach(namespace.properties, function (property, key) { + this.children.push(new Item(this).fromProperty(key, property)); + }, this); + } + + this.sortChildren(); + + return this; + }; + + /** + * Load Item values and child Items from object definition + * + * @param {object} object Metadata object definition + * @returns {Item} + */ + Item.prototype.fromObject = function (object) { + this.label = object.name; + this.description = object.description; + + if(object.properties) { + angular.forEach(object.properties, function (property, key) { + this.children.push(new Item(this).fromProperty(key, property)); + }, this); + } + + this.sortChildren(); + + return this; + }; + + /** + * Load Item values from property definition + * + * @param {string} name Property name + * @param {object} property Metadata property definition + * @returns {Item} + */ + Item.prototype.fromProperty = function (name, property) { + this.leaf = new Property(name, property); + this.label = this.leaf.title; + this.description = this.leaf.description; + + return this; + }; + + /** + * Load Item values from property definition and mark as custom + * + * @param {string} name Property name + * @param {object} property Metadata property definition + * @returns {Item} + */ + Item.prototype.customProperty = function (name, property) { + this.fromProperty(name, property); + this.custom = true; + + return this; + }; + + /** + * Expand Item by marking all children as visible + * + * @param {boolean} deep Whether to recursively expand all child Items + */ + Item.prototype.expand = function (deep) { + this.expanded = true; + angular.forEach(this.children, function (child) { + if(deep) { + child.expand(deep); + } + child.visible = true; + }, this); + }; + + /** + * Collapse Item by recursively unmarking all children as visible + */ + Item.prototype.collapse = function () { + this.expanded = false; + angular.forEach(this.children, function (child) { + child.collapse(); + child.visible = false; + }, this); + }; + + /** + * Sort children Items by label + */ + Item.prototype.sortChildren = function () { + this.children.sort(function (a, b) { + return a.label.localeCompare(b.label); + }); + }; + + /** + * Recursively mark Item and all children as added + * + * @param {=} caller Used internally to prevent infinite recursion + */ + Item.prototype.markAsAdded = function (caller) { + if(this.parent && !this.added) { + this.parent.addedCount += 1; + if(this.parent.addedCount === this.parent.children.length) { + this.parent.markAsAdded(this); + } + } + this.added = true; + if(!caller) { // prevent infinite recursion + angular.forEach(this.children, function (item) { + item.markAsAdded(); + }, this); + } + }; + + /** + * Recursively unmark Item and all children as added + * + * @param {boolean=} expand Whether to expand parent of unmarked Item + * @param {=} caller Used internally to prevent infinite recursion + */ + Item.prototype.unmarkAsAdded = function (expand, caller) { + if(this.parent) { + if(expand) { + this.parent.expand(); + } + if(this.added) { + this.parent.addedCount -= 1; + this.parent.unmarkAsAdded(expand, this); + } + } + this.added = false; + if(!caller) { // prevent infinite recursion + angular.forEach(this.children, function (item) { + item.unmarkAsAdded(expand); + }, this); + } + }; + + /** + * Returns list of Items from top-most parent to this Item + * + * @param {[]=} path Used internally + * @returns {Item[]} + */ + Item.prototype.path = function (path) { + path = typeof path !== 'undefined' ? path : []; + if(this.parent) { + this.parent.path(path); + } + path.push(this); + return path; + }; + + /** + * Returns breadcrumb string for this Item + * + * @returns {string} + */ + Item.prototype.breadcrumb = function () { + return this.path().map(function (item) { + return item.label; + }).join(' › '); + }; + + /** + * Parse string parameter into leaf value + * + * @param {string} value + */ + Item.prototype.setLeafValue = function (value) { + if(this.leaf) { + this.leaf.setValue(value); + } + }; + + /** + * Serialize leaf value into string + * + * @returns {string} + */ + Item.prototype.getLeafValue = function () { + if(this.leaf) { + return this.leaf.getValue(); + } + }; + + /** + * Construct a new tree + * + * @class Tree + * @param {object[]} available List of available namespaces + * @param {object} existing Key-value pairs for existing metadata + * + * @property {Item[]} tree List available namespaces parsed into Item-s + * @property {Item[]} flatTree List of Item-s flattened from tree structure + * @property {Item} selected Selected Item + */ + function Tree(available, existing) { + this.tree = []; + this.loadNamespaces(available); + this.flatTree = this.flatten(this.tree); + this.selected = null; + this.loadExisting(existing); + } + + /** + * Load Item values and child Items from namespace definition + * + * @param {object[]} namespaces list of Metadata namespace definitions + * @returns {Tree} + */ + Tree.prototype.loadNamespaces = function (namespaces) { + angular.forEach(namespaces, function (namespace) { + var item = new Item().fromNamespace(namespace); + item.visible = true; + this.tree.push(item); + }, this); + + this.tree.sort(function (a, b) { + return a.label.localeCompare(b.label); + }); + + return this; + }; + + /** + * Crete flat representation of branch + * + * @param {Item[]} branch List of Items to flatten + * @param {[]=} items Used internally + * @returns {Item[]} + */ + Tree.prototype.flatten = function (branch, items) { + items = typeof items !== 'undefined' ? items : []; + + angular.forEach(branch, function (item) { + items.push(item); + this.flatten(item.children, items); + }, this); + + return items; + }; + + /** + * Load Property.value for each value from existing and mark corresponding + * Items as added. If no corresponding Item is found new Item is added and + * marked as custom. + * + * @param {object} existing + */ + Tree.prototype.loadExisting = function (existing) { + var itemsMapping = {}; + + angular.forEach(this.flatTree, function (item) { + if(item.leaf && item.leaf.name in existing) { + itemsMapping[item.leaf.name] = item; + } + }); + + angular.forEach(existing, function (value, key) { + var item = itemsMapping[key]; + if(typeof item === 'undefined') { + item = new Item().customProperty(key); + this.flatTree.push(item); + } + item.setLeafValue(value); + item.markAsAdded(); + }, this); + }; + + /** + * Returns key-value mapping of leaf Items that was marked as added + * + * @returns {object} + */ + Tree.prototype.getExisting = function () { + var existing = {}; + angular.forEach(this.flatTree, function(item) { + if(item.added && item.leaf) { + existing[item.leaf.name] = item.getLeafValue(); + } + }); + return existing; + }; + + /** + * Selects item and expands / collapses it + * + * @param {Item} item + */ + Tree.prototype.select = function (item) { + this.selected = item; + if(!item.expanded) { + item.expand(); + } else { + item.collapse(); + } + }; + + /** + * Selects item and marks it as added + * + * @param {Item} item + */ + Tree.prototype.markAsAdded = function (item) { + this.selected = item; + item.markAsAdded(); + }; + + /** + * Selects item, unmarks it as added and expands it's parent + * + * @param {Item} item + */ + Tree.prototype.unmarkAsAdded = function (item) { + if(!item.custom) { + this.selected = item; + item.unmarkAsAdded(true); + } else { + this.selected = null; + var i = this.flatTree.indexOf(item); + if(i > -1) { + this.flatTree.splice(i, 1); + } + } + }; + + /** + * Adds new Item, selects it and marks it as custom and added + * + * @param {string} name Name of leaf + */ + Tree.prototype.addCustom = function (name) { + var item = new Item().customProperty(name); + item.markAsAdded(); + this.flatTree.push(item); + this.selected = item; + }; + + return { + Item: Item, + Property: Property, + Tree: Tree + }; + }]); +}()); diff -Nru horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree.spec.js horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree.spec.js --- horizon-2015.1~b2/horizon/static/angular/metadata-tree/metadata-tree.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/metadata-tree/metadata-tree.spec.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,150 @@ +/* jshint globalstrict: true */ +'use strict'; + +describe('hz.widget.metadata-tree module', function() { + it('should have been defined', function () { + expect(angular.module('hz.widget.metadata-tree')).toBeDefined(); + }); + + var namespaces = [ + { + "display_name": "Test Namespace A", + "description": "Test namespace description", + "properties": { + "test:A:1": { + "title": "Test A.1 - string", + "type": "string", + "default": "foo", + "enum": [ + "option-1", "option-2", "option-3" + ] + }, + "test:A:2": { + "title": "Test A.2 - integer", + "type": "integer", + "default": "1", + "minimum": 0, + "maximum": 10 + }, + "test:A:3": { + "title": "Test A.3 - number", + "type": "number", + "default": "1.1", + "minimum": 0, + "maximum": 10 + }, + "test:A:4": { + "title": "Test A.4 - boolean", + "type": "boolean", + "default": "True" + }, + "test:A:5": { + "title": "Test A.5 - boolean", + "type": "boolean", + "default": "false" + }, + "test:A:6": { + "title": "Test A.6 - array", + "type": "array", + "items": { + "type": "string", + "enum": [ + "val-1", "val-2", "val-3" + ] + } + } + } + }, + { + "display_name": "Test Namespace B", + "description": "Test namespace description", + "objects": [ + { + "name": "Test Object A", + "description": "Test object description", + "properties": { + "test:B:A:1": { + "title": "Test B.A.1", + "description": "Test description" + }, + "test:B:A:2": {} + } + }, + { + "name": "Test Object B", + "description": "Test object description", + "properties": { + "test:B:B:1": {}, + "test:B:B:2": {} + } + } + ] + } + ]; + + describe('directives', function () { + var $scope, $element; + + beforeEach(module('templates')); + beforeEach(module('hz')); + beforeEach(module('hz.widgets')); + beforeEach(module('hz.widget.metadata-tree')); + + describe('hzMetadataTree directive', function() { + beforeEach(inject(function ($injector) { + var $compile = $injector.get('$compile'); + $scope = $injector.get('$rootScope').$new(); + + $scope.available = namespaces; + $scope.existing = {'test:B:A:1':'foo'}; + + var markup = + ''; + + $element = angular.element(markup); + $compile($element)($scope); + $scope.$digest(); + })); + + it('should have 2 rows in available list', function() { + expect($element.find('ul.list-group:first li[ng-repeat]').length).toBe(2); + }); + + it('should have 1 row in existing list', function() { + expect($element.find('ul.list-group:last li[ng-repeat]').length).toBe(1); + expect($element.find('ul.list-group:last li[ng-repeat]:first').scope().item.leaf.name).toBe('test:B:A:1'); + expect($element.find('ul.list-group:last li[ng-repeat]:first').scope().item.leaf.value).toBe('foo'); + }); + + it('should have 10 rows in available list when expanded items', function() { + $element.find('ul.list-group:first li[ng-repeat]:first').trigger('click'); + $element.find('ul.list-group:first li[ng-repeat]:last').trigger('click'); + expect($element.find('ul.list-group:first li[ng-repeat]').length).toBe(10); + }); + + it('should remove item from available and add it in existing list when added', function() { + $element.find('ul.list-group:first li[ng-repeat]:last').trigger('click'); + $element.find('ul.list-group:first li[ng-repeat]:last').trigger('click'); + expect($element.find('ul.list-group:first li[ng-repeat]').length).toBe(6); + $element.find('ul.list-group:first li[ng-repeat]:last .btn').trigger('click'); + expect($element.find('ul.list-group:first li[ng-repeat]').length).toBe(5); + expect($element.find('ul.list-group:last li[ng-repeat]').length).toBe(2); + expect($element.find('ul.list-group:last li[ng-repeat].active').scope().item.leaf.name).toBe('test:B:B:2'); + }); + + it('should add item to available and remove it from existing list when removed', function() { + $element.find('ul.list-group:last li[ng-repeat]:first .btn').trigger('click'); + expect($element.find('ul.list-group:first li[ng-repeat]').length).toBe(6); + expect($element.find('ul.list-group:last li[ng-repeat]').length).toBe(0); + expect($element.find('ul.list-group:first li[ng-repeat].active').scope().item.leaf.name).toBe('test:B:A:1'); + }); + + it('should add custom item to existing list', function() { + $element.find('ul.list-group:first li:first input').val('custom').trigger('input'); + $element.find('ul.list-group:first li:first .btn').trigger('click'); + expect($element.find('ul.list-group:last li[ng-repeat]').length).toBe(2); + expect($element.find('ul.list-group:last li[ng-repeat].active').scope().item.leaf.name).toBe('custom'); + }); + }); + }); +}); diff -Nru horizon-2015.1~b2/horizon/static/angular/modal/modal.js horizon-2015.1~b3/horizon/static/angular/modal/modal.js --- horizon-2015.1~b2/horizon/static/angular/modal/modal.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/modal/modal.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,95 @@ +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name hz.widget.modal + * + * # hz.widget.modal + * + * The `hz.widget.modal` provides modal services. + * + * Requires {@link http://angular-ui.github.io/bootstrap/ `Angular-bootstrap`} + * + * | Components | + * |--------------------------------------------------------------------------| + * | {@link hz.widget.modal.controller:simpleModalCtrl `simpleModalCtrl`} | + * | {@link hz.widget.modal.factory:simpleModalService `simpleModalService`} | + * + */ + angular.module('hz.widget.modal', ['ui.bootstrap']) + + /** + * @ngdoc controller + * @name simpleModalCtrl + * + * @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. + */ + .controller('simpleModalCtrl', [ '$scope', '$modalInstance', 'context', + function($scope, $modalInstance, context) { + $scope.context = context; + $scope.submit = function(){ $modalInstance.close(); }; + $scope.cancel = function(){ $modalInstance.dismiss('cancel'); }; + } // end of function + ]) // end of controller + + /** + * @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', [ '$scope', 'simpleModalService', + * function($scope, simpleModalService){ + * var options = { + * title: 'Confirm Delete', + * body: 'Are you sure you want to delete this item?', + * submit: 'Yes', + * cancel: 'No', + * }; + * simpleModalService(options).result.then(function(){ + * // user clicked on submit button + * // do something useful here + * }); + * } + * ]); + */ + .factory('simpleModalService', ['$modal', 'basePath', + function($modal, path) { + return function(params) { + if (params && params.title && params.body){ + var options = { + controller: 'simpleModalCtrl', + templateUrl: path + 'modal/simple-modal.html', + resolve: { + context: function() { + return { + title: params.title? params.title: '', + body: params.body? params.body: '', + submit: params.submit? params.submit: gettext('Submit'), + cancel: gettext('Cancel') + }; + } + } + }; + return $modal.open(options); + } + }; // end of return + } // end of function + ]); // end of factory + +})(); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/modal/simple-modal.html horizon-2015.1~b3/horizon/static/angular/modal/simple-modal.html --- horizon-2015.1~b2/horizon/static/angular/modal/simple-modal.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/modal/simple-modal.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/modal/simple-modal.spec.js horizon-2015.1~b3/horizon/static/angular/modal/simple-modal.spec.js --- horizon-2015.1~b2/horizon/static/angular/modal/simple-modal.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/modal/simple-modal.spec.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,56 @@ +/* jshint globalstrict: true */ +'use strict'; + +describe('hz.widget.modal module', function(){ + it('should have been defined', function(){ + expect(angular.module('hz.widget.modal')).toBeDefined(); + }); +}); + +describe('modal factory', function(){ + + var modal; + var modalData = { + title: 'Confirm deletion', + body: 'Are you sure?', + submit: 'Yes', + cancel: 'No' + }; + + beforeEach(module('ui.bootstrap')); + beforeEach(module('hz')); + beforeEach(module('hz.widgets')); + beforeEach(module('hz.widget.modal')); + beforeEach(inject(function($injector, simpleModalService){ + modal = simpleModalService; + })); + + it('should fail without any params', function(){ + expect(modal()).toBeUndefined(); + }); + + it('should fail without a title', function(){ + var data = angular.copy(modalData); + delete data.title; + expect(modal(data)).toBeUndefined(); + }); + + it('should fail without a body', function(){ + var data = angular.copy(modalData); + delete data.body; + expect(modal(data)).toBeUndefined(); + }); + + it('should have default submit and cancel labels', function(){ + var data = angular.copy(modalData); + delete data.submit; + delete data.cancel; + expect(modal(data)).toBeDefined(); + }); + + it('should work when all params are supplied', function(){ + var data = angular.copy(modalData); + expect(modal(data)).toBeDefined(); + }); + +}); diff -Nru horizon-2015.1~b2/horizon/static/angular/modal-wait-spinner/modal-wait-spinner.js horizon-2015.1~b3/horizon/static/angular/modal-wait-spinner/modal-wait-spinner.js --- horizon-2015.1~b2/horizon/static/angular/modal-wait-spinner/modal-wait-spinner.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/modal-wait-spinner/modal-wait-spinner.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,113 @@ +/* + * (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 hz.widget.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. + + @example + +
+    .controller('MyController', [
+      '$scope',
+      'modalWaitSpinnerService',
+      function (modalWaitSpinnerService) {
+        $scope.showSpinner = function () {
+          modalWaitSpinnerService.showModalSpinner(gettext("Loading"));
+        }
+        $scope.hideSpinner = function () {
+          modalWaitSpinnerService.hideModalSpinner();
+         }
+      }
+    ])
+   
+
+ + 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. + */ + + angular.module('hz.widget.modal-wait-spinner', [ + 'ui.bootstrap', + ]) + .factory('modalWaitSpinnerService', [ + '$modal', + function ($modal) { + var modalInstance; + + var service = { + showModalSpinner: function (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' + }; + this.modalInstance = $modal.open(modalOptions); + }, + + hideModalSpinner: function () { + if (this.modalInstance) { + this.modalInstance.dismiss(); + delete(this.modalInstance); + } + } + }; + + return service; + } + ]) + + .directive('waitSpinner', function () { + + return { + scope: { + text: '@text' // One-direction binding (reads from parent) + }, + restrict: 'A', + link: link, + template: '

{$text$}…

' + }; + + function link($scope, element, attrs) { + element.spin(horizon.conf.spinner_options.modal); + /* + 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 + */ + element.find('.spinner').css({'left': '50%', 'top': '50%'}); + } + }); +})(); diff -Nru horizon-2015.1~b2/horizon/static/angular/modal-wait-spinner/modal-wait-spinner.scss horizon-2015.1~b3/horizon/static/angular/modal-wait-spinner/modal-wait-spinner.scss --- horizon-2015.1~b2/horizon/static/angular/modal-wait-spinner/modal-wait-spinner.scss 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/modal-wait-spinner/modal-wait-spinner.scss 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * (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 { + transform: translate(0, 0); +} \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/styles.scss horizon-2015.1~b3/horizon/static/angular/styles.scss --- horizon-2015.1~b2/horizon/static/angular/styles.scss 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/styles.scss 2015-03-19 19:07:29.000000000 +0000 @@ -1 +1,10 @@ -@import "help-panel/help-panel"; \ No newline at end of file +@import "help-panel/help-panel"; +@import "wizard/wizard"; +@import "table/table"; +@import "transfer-table/transfer-table"; +@import "charts/chart-tooltip"; +@import "charts/pie-chart"; +@import "action-list/action-list"; +@import "modal-wait-spinner/modal-wait-spinner"; +@import "metadata-tree/metadata-tree"; +@import "metadata-display/metadata-display"; diff -Nru horizon-2015.1~b2/horizon/static/angular/table/basic-table.js horizon-2015.1~b3/horizon/static/angular/table/basic-table.js --- horizon-2015.1~b2/horizon/static/angular/table/basic-table.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/table/basic-table.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,48 @@ +(function() { + 'use strict'; + + angular.module('hz.widget.table') + + /** + * @ngdoc directive + * @name hz.widget.table.directive:searchBar + * @element + * @param {string} {array} groupClasses Input group classes (optional) + * @param {string} {array} iconClasses Icon classes (optional) + * @param {string} {array} inputClasses Search field classes (optional) + * @description + * The `searchBar` directive generates a search field that will + * trigger filtering of the associated Smart-Table. + * + * groupClasses - classes that should be applied to input group element + * iconClasses - classes that should be applied to search icon + * inputClasses - classes that should be applied to search input field + * + * @restrict E + * + * @example + * ``` + * + * + * ``` + */ + .directive('searchBar', [ 'basePath', function(path) { + return { + restrict: 'E', + templateUrl: path + 'table/search-bar.html', + link: function (scope, element, attrs) { + if (angular.isDefined(attrs.groupClasses)) { + element.find('.input-group').addClass(attrs.groupClasses); + } + if (angular.isDefined(attrs.iconClasses)) { + element.find('.fa').addClass(attrs.iconClasses); + } + if (angular.isDefined(attrs.inputClasses)) { + element.find('[st-search]').addClass(attrs.inputClasses); + } + } + }; + }]); + +}()); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/table/basic-table.spec.js horizon-2015.1~b3/horizon/static/angular/table/basic-table.spec.js --- horizon-2015.1~b2/horizon/static/angular/table/basic-table.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/table/basic-table.spec.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,57 @@ +(function() { + 'use strict'; + + describe('search bar directive', function() { + + var $scope, $element; + + beforeEach(module('templates')); + beforeEach(module('smart-table')); + beforeEach(module('hz')); + beforeEach(module('hz.widgets')); + beforeEach(module('hz.widget.table')); + + describe('search bar', function() { + + beforeEach(inject(function($injector) { + var $compile = $injector.get('$compile'); + $scope = $injector.get('$rootScope').$new(); + + $scope.rows = []; + + var markup = '' + + '' + + ' ' + + ' ' + + ' ' + + '' + + '' + + '
' + + ' ' + + ' ' + + '
'; + + $element = angular.element(markup); + $compile($element)($scope); + + $scope.$digest(); + })); + + it('should have a text field', function() { + expect($element.find('input[st-search]').length).toBe(1); + }); + + it('should have a search icon', function() { + expect($element.find('.input-group-addon .fa-search').length).toBe(1); + }); + + it('should have a "input-group-sm" class on input group', function() { + expect($element.find('.input-group.input-group-sm').length).toBe(1); + }); + + }); + + }); + +})(); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/table/search-bar.html horizon-2015.1~b3/horizon/static/angular/table/search-bar.html --- horizon-2015.1~b2/horizon/static/angular/table/search-bar.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/table/search-bar.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,8 @@ + \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/table/table.js horizon-2015.1~b3/horizon/static/angular/table/table.js --- horizon-2015.1~b2/horizon/static/angular/table/table.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/table/table.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,236 @@ +/* jshint globalstrict: true */ +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name hz.widget.table + * @description + * + * # hz.widget.table + * + * The `hz.widget.table` provides support for user interactions and checkbox + * selection in tables. + * + * Requires {@link https://github.com/lorenzofox3/Smart-Table `Smart-Table`} + * module and jQuery (for table drawer slide animation in IE9) to be installed. + * + * | Directives | + * |-------------------------------------------------------------------| + * | {@link hz.widget.table.directive:hzTable `hzTable`} | + * | {@link hz.widget.table.directive:hzSelectAll `hzSelectAll`} | + * | {@link hz.widget.table.directive:hzExpandDetail `hzExpandDetail`} | + * + */ + var app = angular.module('hz.widget.table', [ 'smart-table', 'lrDragNDrop' ]); + + /** + * @ngdoc parameters + * @name hz.widget.table.constant:expandSettings + * @param {string} expandIconClasses Icon classes to be used for expand icon + * @param {number} duration The slide down animation duration/speed + */ + app.constant('expandSettings', { + expandIconClasses: 'fa-chevron-right fa-chevron-down', + duration: 400 + }); + + /** + * @ngdoc directive + * @name hz.widget.table.directive:hzTable + * @element table st-table='rowCollection' + * @description + * The `hzTable` directive extends the Smart-Table module to provide + * support for saving the checkbox selection state of each row in the + * table. Also included is the `updateSelectCount` function which + * updates the checkbox selection count of the table. A default sort + * key can be specified to sort the table initially by this key. To + * reverse the sort, add default-sort-reverse='true' as well. + * + * @restrict A + * @scope true + * @example + * + * ``` + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Name
+ * + * Foo
+ * ``` + * + */ + app.directive('hzTable', function() { + return { + restrict: 'A', + require: 'stTable', + scope: true, + controller: function($scope) { + $scope.selected = {}; + $scope.numSelected = 0; + + $scope.updateSelectCount = function(row) { + if ($scope.selected.hasOwnProperty(row.id)) { + var checkedState = $scope.selected[row.id].checked; + + if (checkedState) { + $scope.numSelected += 1; + } else { + $scope.numSelected -= 1; + } + } + }; + + this.select = function(row, checkedState) { + var oldCheckedState = $scope.selected.hasOwnProperty(row.id) ? + $scope.selected[row.id].checked : + false; + + $scope.selected[row.id] = { + checked: checkedState, + item: row + }; + + if (checkedState && !oldCheckedState) { + $scope.numSelected += 1; + } else if (!checkedState && oldCheckedState) { + $scope.numSelected -= 1; + } + }; + }, + link: function(scope, element, attrs, stTableCtrl) { + if (attrs.defaultSort) { + var reverse = attrs.defaultSortReverse === 'true'; + stTableCtrl.sortBy(attrs.defaultSort, reverse); + } + } + }; + }); + + /** + * @ngdoc directive + * @name hz.widget.table.directive:hzSelectAll + * @element input type='checkbox' + * @description + * The `hzSelectAll` directive updates the checkbox selection state of + * every row in the table. Assign this as an attribute to a checkbox + * input element, passing in the row collection data. + * + * @restrict A + * @scope rows: '=hzSelectAll' + * @example + * + * ``` + * + * ``` + * + */ + app.directive('hzSelectAll', function() { + return { + restrict: 'A', + require: '^hzTable', + scope: { + rows: '=hzSelectAll' + }, + link: function(scope, element, attrs, hzTableCtrl) { + element.on('click', function() { + scope.$apply(function() { + var checkedState = element.prop('checked'); + angular.forEach(scope.rows, function(row) { + hzTableCtrl.select(row, checkedState); + }); + }); + }); + } + }; + }); + + /** + * @ngdoc directive + * @name hz.widget.table.directive:hzExpandDetail + * @element i class='fa fa-chevron-right' + * @param {number} duration The duration for drawer slide animation + * @description + * The `hzExpandDetail` directive toggles the detailed drawer of the row. + * The animation is implemented using jQuery's slideDown() and slideUp(). + * Assign this as an attribute to an icon that should trigger the toggle, + * passing in the two class names of the icon. If no class names are + * specified, the default 'fa-chevron-right fa-chevron-down' is used. A + * duration for the slide animation can be specified as well (default: 400). + * The detail drawer row and cell also needs to be implemented and include + * the classes 'detail-row' and 'detail', respectively. + * + * @restrict A + * @scope icons: '@hzExpandDetail', duration: '@' + * @example + * + * ``` + * + * + * + * + * + * + * + * + * ``` + * + */ + app.directive('hzExpandDetail', [ 'expandSettings', function(settings) { + return { + restrict: 'A', + scope: { + icons: '@hzExpandDetail', + duration: '@' + }, + link: function(scope, element) { + element.on('click', function() { + var iconClasses = scope.icons || settings.expandIconClasses; + element.toggleClass(iconClasses); + + var summaryRow = element.closest('tr'); + var detailCell = summaryRow.next('tr').find('.detail'); + var duration = scope.duration ? parseInt(scope.duration) : settings.duration; + + if (summaryRow.hasClass('expanded')) { + var options = { + duration: duration, + complete: function() { + // Hide the row after the slide animation finishes + summaryRow.toggleClass('expanded'); + } + }; + + detailCell.find('.detail-expanded').slideUp(options); + } else { + summaryRow.toggleClass('expanded'); + + if (detailCell.find('.detail-expanded').length === 0) { + // Slide down animation doesn't work on table cells + // so a
wrapper needs to be added + detailCell.wrapInner('
'); + } + + detailCell.find('.detail-expanded').slideDown(duration); + } + }); + } + }; + }]); + +})(); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/table/table.scss horizon-2015.1~b3/horizon/static/angular/table/table.scss --- horizon-2015.1~b2/horizon/static/angular/table/table.scss 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/table/table.scss 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,348 @@ +$em-per-priority: floor($table-col-avg-width / $font-size-base) * 3; + +[hz-table] { + td.action-col { + .popover { + min-width: 15em; + } + } + + .detail-row td { + display: none; + + &.detail .detail-expanded { + display: none; + } + } + + .expanded + tr td { + display: table-cell; + } + + .fa { + cursor: pointer; + } + + .invalid { + color: $invalid-color; + } + + .no-rows-help { + font-style: italic; + font-weight: normal; + text-align: center; + } + + .reorder { + min-width: 4em; + max-width: 4em; + width: 4em; + } + + .search-header { + padding: 0; + + .btn-addon { + display: table-cell; + padding-left: 0.3em; + vertical-align: top; + width: 1%; + } + + .search-bar { + margin-bottom: 0.2em; + width: 100%; + + .input-group-sm .form-control:not(:first-child):not(:last-child) { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + } + } + + .search-help { + color: $transfer-help-text-color; + font-size: 0.9em; + font-style: italic; + font-weight: 400; + margin: 0.2em 0.3em 0; + visibility: hidden; + + &.searching { + visibility: visible; + } + } + } +} + +.table-rsp { + border-collapse: separate; + border-spacing: 0 $table-gap-height; + width: 100%; + + thead tr th, tfoot tr td { + background: none; + border: none; + padding: $table-padding; + } + + tbody tr { + &[lr-drag-src] td:not(.expander) { + cursor: move; + } + + &.lr-drop-target-before td { + border-top: $reorder-border !important; + } + + &.lr-drop-target-after td { + border-bottom: $reorder-border !important; + } + + td { + background-color: #ffffff; + border-top: $table-border; + border-bottom: $table-border; + padding: $table-padding; + position: relative; + white-space: nowrap; + + &:first-child, &.action-col { + border-left: $table-border; + } + + &:last-child, &.select-col { + border-right: $table-border; + } + } + } + + .select-col { + max-width: $select-col-width; + text-align: center; + width: $select-col-width; + } + + .action-col { + position: relative; + text-align: right; + vertical-align: top; + min-width: $batch-action-width; + width: $batch-action-width; + } + + .numeric { + text-align: right; + } + + [st-sort] { + cursor: pointer; + + &:after { + color: #d4d4d4; + content: '\f0dc'; + font-family: 'FontAwesome'; + margin-left: 0.5em; + opacity: 0; + } + + &:not(.st-sort-ascent):hover:after, &:not(.st-sort-descent):hover:after { + opacity: 1; + } + } + + .st-sort-ascent:after { + color: #000000; + content: '\f0dd'; + font-family: 'FontAwesome'; + margin-left: 0.5em; + opacity: 1; + } + + .st-sort-descent:after { + color: #000000; + content: '\f0de'; + font-family: 'FontAwesome'; + margin-left: 0.5em; + opacity: 1; + } + + &.modern { + border-spacing: 0; + + tbody tr { + td { + border: none; + border-top: $table-border; + } + + &:last-child td { + border-bottom: $table-border; + } + } + } + + &.table-detail { + border-spacing: 0; + + tbody { + tr td { + border-bottom: none; + } + + tr:last-child:not(.spacer-row) td { + border-bottom: $table-border; + } + + tr.expanded td { + border-bottom: $table-border; + + &[rowspan='2'].action-col { + border-bottom: none; + } + } + + tr.expanded:nth-last-child(-n+3) [rowspan='2'].action-col { + border-bottom: $table-border; + } + + tr:nth-last-child(2):not(.expanded) td { + border-bottom: $table-border; + } + + tr:nth-last-child(3).expanded + .detail-row + tr.spacer-row td { + border-top: none; + } + + tr + .detail-row + tr.spacer-row td { + border-top: $table-border; + } + } + + .detail-row td { + padding: 0; + + &.detail .detail-expanded { + border-top: none; + padding: $detail-row-padding $table-padding; + white-space: normal; + } + } + + .expanded + tr td { + border-top: none; + } + + .expander { + cursor: pointer; + max-width: $expander-width; + width: $expander-width; + } + + .spacer-row td { + background-color: inherit; + border: none; + height: $table-gap-height; + padding: 0; + position: relative; + } + + &.table-striped { + tbody { + tr { + &:nth-child(2n+1) > td, &:nth-child(2n+1) + .detail-row > td { + background-color: $table-stripe-bgcolor; + } + + &.spacer-row > td, &.spacer-row:nth-child(6n+3) + tr + tr.detail-row td, + &.detail-row:nth-child(4n+2) + tr:not(.spacer-row) td, + &.detail-row:nth-child(4n+2) + tr:not(.spacer-row) + tr.detail-row td { + background-color: transparent; + } + } + } + } + + &.modern { + .expanded + tr td { + border-top: $table-border; + } + + .expanded { + td:not(.action-col), td.action-col:not([rowspan='2']) { + border-bottom: none; + } + } + } + } + + @media only all { + .rsp-p1, .rsp-p2, + .rsp-p3, .rsp-p4 { + display: none; + } + + .rsp-alt-p1, .rsp-alt-p2, + .rsp-alt-p3, .rsp-alt-p4 { + display: inline-block; + } + } + + @media (min-width: 0em) { + $content-width: $body-min-width - $sidebar-width - (2 * $content-body-padding); + $max-priority: floor($content-width / $font-size-base / $em-per-priority); + + @for $i from 1 through $max-priority { + .rsp-p#{$i} { + display: table-cell; + } + + .rsp-alt-p#{$i} { + display: none; + } + } + } + + $p1-width: 5em + $em-per-priority * 1em; + @media (min-width: $p1-width) { + .rsp-p1 { + display: table-cell; + } + + .rsp-alt-p1 { + display: none; + } + } + + $p2-width: 5em + $em-per-priority * 2em; + @media (min-width: $p2-width) { + .rsp-p2 { + display: table-cell; + } + + .rsp-alt-p2 { + display: none; + } + } + + $p3-width: 5em + $em-per-priority * 3em; + @media (min-width: $p3-width) { + .rsp-p3 { + display: table-cell; + } + + .rsp-alt-p3 { + display: none; + } + } + + $p4-width: 5em + $em-per-priority * 4em; + @media (min-width: $p4-width) { + .rsp-p4 { + display: table-cell; + } + + .rsp-alt-p4 { + display: none; + } + } +} \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/table/table.spec.js horizon-2015.1~b3/horizon/static/angular/table/table.spec.js --- horizon-2015.1~b2/horizon/static/angular/table/table.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/table/table.spec.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,144 @@ +/* jshint browser: true, globalstrict: true */ +'use strict'; + +describe('hz.widget.table module', function() { + it('should have been defined".', function () { + expect(angular.module('hz.widget.table')).toBeDefined(); + }); +}); + +describe('table directives', function () { + + var $scope, $element; + + beforeEach(module('smart-table')); + beforeEach(module('hz')); + beforeEach(module('hz.widgets')); + beforeEach(module('hz.widget.table')); + + 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 = + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Animal
{{ row.animal }}
'; + + $element = angular.element(markup); + $compile($element)($scope); + + $scope.$digest(); + })); + + describe('hzTable directive', function() { + + it('should have 3 summary rows', function() { + expect($element.find('tbody tr[ng-repeat-start]').length).toBe(3); + }); + + it('should have 3 detail rows', function() { + expect($element.find('tbody tr.detail-row').length).toBe(3); + }); + + it('should have each checkbox initially unchecked', function() { + var checkboxes = $element.find('tbody tr[ng-repeat-start] input[type="checkbox"]'); + angular.forEach(checkboxes, function(checkbox) { + expect(checkbox.checked).toBe(false); + }); + }); + + it('should have numSelected === 1 when first checkbox is clicked', function() { + var firstInput = $element.find('tbody input[type="checkbox"]').first(); + var ngModelCtrl = firstInput.controller('ngModel'); + ngModelCtrl.$setViewValue(true); + + $scope.$digest(); + + expect($element.scope().numSelected).toBe(1); + }); + + it('should have numSelected === 3 and select-all checkbox checked when all rows selected', function() { + var checkboxes = $element.find('tbody input[type="checkbox"]'); + angular.forEach(checkboxes, function(checkbox) { + checkbox.checked = true; + var ngModelCtrl = angular.element(checkbox).controller('ngModel'); + ngModelCtrl.$setViewValue(true); + }); + + $scope.$digest(); + + expect($element.scope().numSelected).toBe(3); + expect($element.find('thead input[hz-select-all]')[0].checked).toBe(true); + }); + + }); + + describe('hzSelectAll directive', function() { + + it('should select all checkboxes if select-all checkbox checked', function() { + var selectAllCb = $element.find('input[hz-select-all]').first(); + selectAllCb[0].checked = true; + selectAllCb.triggerHandler('click'); + + $scope.$digest(); + + expect($element.scope().numSelected).toBe(3); + var checkboxes = $element.find('tbody input[type="checkbox"]'); + angular.forEach(checkboxes, function(checkbox) { + expect(checkbox.checked).toBe(true); + }); + }); + + }); + + describe('hzExpandDetail directive', function() { + + it('should have summary row with class "expanded" when expanded', function() { + var expandIcon = $element.find('i.fa').first(); + expandIcon.click(); + + var summaryRow = expandIcon.closest('tr'); + expect(summaryRow.hasClass('expanded')).toBe(true); + }); + + it('should have summary row without class "expanded" when not expanded', function(done) { + var expandIcon = $element.find('i.fa').first(); + + // Click twice to mock expand and collapse + expandIcon.click(); + expandIcon.click(); + + // Wait for the slide down animation to complete before test + setTimeout(function() { + var summaryRow = expandIcon.closest('tr'); + expect(summaryRow.hasClass('expanded')).toBe(false); + done(); + }, 2000); + }); + + }); + +}); diff -Nru horizon-2015.1~b2/horizon/static/angular/transfer-table/allocated.html.example horizon-2015.1~b3/horizon/static/angular/transfer-table/allocated.html.example --- horizon-2015.1~b2/horizon/static/angular/transfer-table/allocated.html.example 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/transfer-table/allocated.html.example 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameVCPUsRAMTotal DiskRoot DiskEphemeral DiskPublic
+
+ {$ ::trCtrl.helpText.noneAllocText $} +
+
+ + {$ row.name $}{$ row.vcpus $}{$ row.ram $}{$ row.totalDisk $}{$ row.rootDisk $}{$ row.ephemeralDisk $}{$ row.isPublic $} + + + + + +
+
    +
  • Name: {$ row.name $}
  • +
  • Name: {$ row.vcpus $}
  • +
  • Name: {$ row.ram $}
  • +
  • Name: {$ row.totalDisk $}
  • +
  • Name: {$ row.rootDisk $}
  • +
  • Name: {$ row.ephemeralDisk $}
  • +
  • Name: {$ row.isPublic $}
  • +
+
\ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/transfer-table/available.html.example horizon-2015.1~b3/horizon/static/angular/transfer-table/available.html.example --- horizon-2015.1~b2/horizon/static/angular/transfer-table/available.html.example 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/transfer-table/available.html.example 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
NameVCPUsRAMTotal DiskRoot DiskEphemeral DiskPublic
+
+ {$ ::trCtrl.helpText.noneAvailText $} +
+
+ + {$ row.name $} + + {$ row.vcpus $} + + + {$ row.ram $} + {$ row.totalDisk $}{$ row.rootDisk $}{$ row.ephemeralDisk $}{$ row.isPublic $} + + + + + + + + +
+
    +
  • Name: {$ row.name $}
  • +
  • Name: {$ row.vcpus $}
  • +
  • Name: {$ row.ram $}
  • +
  • Name: {$ row.totalDisk $}
  • +
  • Name: {$ row.rootDisk $}
  • +
  • Name: {$ row.ephemeralDisk $}
  • +
  • Name: {$ row.isPublic $}
  • +
+
\ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/transfer-table/transfer-table.html horizon-2015.1~b3/horizon/static/angular/transfer-table/transfer-table.html --- horizon-2015.1~b2/horizon/static/angular/transfer-table/transfer-table.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/transfer-table/transfer-table.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,48 @@ +
+ +
+ +
+ + {$ ::trCtrl.helpText.allocTitle $} + {$ trCtrl.numAllocated() $} + + {$ ::trCtrl.helpText.allocHelpText $} + +
+ +
+ {$ ::trCtrl.helpText.allocHiddenText $} +
+
+ +
+
+ + +
+ +
+ + {$ ::trCtrl.helpText.availTitle $} + {$ trCtrl.numAvailable $} + + {$ ::trCtrl.helpText.availHelpText $} + +
+ +
+ {$ ::trCtrl.helpText.availHiddenText $} +
+
+ +
+
+
\ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/transfer-table/transfer-table.js horizon-2015.1~b3/horizon/static/angular/transfer-table/transfer-table.js --- horizon-2015.1~b2/horizon/static/angular/transfer-table/transfer-table.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/transfer-table/transfer-table.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,407 @@ +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name hz.widget.transfer-table + * @description + * + * # hz.widget.transfer-table + * + * The `hz.widget.transfer-table` module provides support for transferring + * rows between two tables (allocated and available). + * + * Requires {@link hz.widget.table.directive:hzTable `hzTable`} module to + * be installed. + * + * | Directives | + * |--------------------------------------------------------------------------| + * | {@link hz.widget.transfer-table.directive:transferTable `transferTable`} | + * + */ + angular.module('hz.widget.transfer-table', [ 'ui.bootstrap' ]) + + /** + * @ngdoc parameters + * @name hz.widget.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('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 hz.widget.transfer-table.constant:limits + * @param {number} maxAllocation Maximum allocation allowed + */ + .constant('limits', { + maxAllocation: 1 + }) + + /* + * @ngdoc filter + * @name hz.widget.transfer-table.filter:warningText + * @returns {string} Warning text if exists or empty string + */ + .filter('warningText', function() { + return function(input, key) { + if (input && input.hasOwnProperty(key)) { + return input[key]; + } + return ''; + }; + }) + + /** + * @ngdoc filter + * @name hz.widget.transfer-table.filter:rowFilter + * @returns {array} List of filtered items based on field passed in + */ + .filter('rowFilter', function() { + return function(items, field) { + if (field) { + return items.filter(function(item) { + return !item[field]; + }); + } else { + return items; + } + }; + }) + + /** + * @ngdoc filter + * @name hz.widget.transfer-table.filter:foundText + * @returns {string} Help text for filter results + */ + .filter('foundText', function() { + return function(foundItems, total) { + var numFound = foundItems.length; + var transObj = { found: numFound, total: total }; + var message = gettext('Found %(found)s of %(total)s'); + return interpolate(message, transObj, true); + }; + }) + + /** + * @ngdoc controller + * @name hz.widget.transfer-table.controller:transferTableCtrl + * @description + * The `transferTableCtrl` controller provides functions for allocating + * and deallocating to and from the 'allocated' array, respectively. + * + * This controller can be accessed through `trCtrl`. See examples below. + * + * Functions and objects available: + * + * allocate - add row to allocated array + * Provide this as callback for the allocate button + * + * + * + * + * deallocate - remove row from allocated array + * Provide this as callback for the deallocate button + * + * + * + * + * updateAllocated - update allocated array after re-order + * This is needed if drag and drop re-ordering is enabled in + * the allocated table. + * + * ... table definition ... + *
+ * + * tooltipModel - custom warning tooltip model + * Use this with the allocate button (action-list) + * + * ... + * + * + */ + .controller('transferTableCtrl', + [ 'basePath', '$scope', '$parse', '$attrs', 'helpText', 'limits', + function(path, $scope, $parse, $attrs, helpText, limits) { + var trModel = $parse($attrs.trModel)($scope); + var trHelpText = $parse($attrs.helpText)($scope); + var trLimits = $parse($attrs.limits)($scope); + + if (!angular.isArray(trModel.allocated)) { + console.error('Allocated is not an array as required.'); + } + + var model = this; + model.helpText = angular.extend({}, helpText, trHelpText); + model.limits = angular.extend({}, limits, trLimits); + model.numAvailable = trModel.available ? trModel.available.length : 0; + model.views = { allocated: true, available: true }; + + // Tooltip model + var clkMsg = 'Click here to expand the row and view the error messages.'; + model.tooltipModel = { + templateUrl: path + 'action-list/warning-tooltip.html', + data: { + clickMessage: gettext(clkMsg), + expandDetail: function() { + var row = this.element.closest('tr'); + if (!row.hasClass('expanded')) { + row.find('[hz-expand-detail]').click(); + } + } + } + }; + + function setAllocatedIds(allocatedRows) { + model.allocatedIds = {}; + if (allocatedRows) { + angular.forEach(allocatedRows, function(alloc) { + model.allocatedIds[alloc.id] = true; + }); + + if (trModel.available) { + model.numAvailable = trModel.available.length - allocatedRows.length; + } else { + model.numAvailable = 0; + } + } else { + trModel.allocated = []; + trModel.displayedAllocated = []; + } + } + + // Update tracking of allocated IDs when allocated changed + $scope.$watchCollection(function() { + return trModel.allocated; + }, function(newAllocated) { + setAllocatedIds(newAllocated); + }); + + // Update available count when available changed + $scope.$watchCollection(function() { + return trModel.available; + }, function(newAvailable) { + var numAvailable = newAvailable ? newAvailable.length : 0; + var numAllocated = trModel.allocated ? trModel.allocated.length : 0; + model.numAvailable = numAvailable - numAllocated; + }); + + // Initialize tracking of allocated IDs + setAllocatedIds(trModel.allocated); + + model.allocate = function(row) { + if (model.limits.maxAllocation < 0 || + trModel.allocated.length < model.limits.maxAllocation) { + // Add to allocated only if limit not reached + trModel.allocated.push(row); + + model.numAvailable -= 1; + } else if (model.limits.maxAllocation === 1) { + // Swap out rows if only one allocation allowed + var oldRow = trModel.allocated.pop(); + + // When swapping out, Smart-Table $watch is + // not detecting change so timeout is used + // as workaround. + setTimeout(function() { + trModel.allocated.push(row); + $scope.$apply(); + }, 1); + } + }; + + model.deallocate = function(row) { + model.numAvailable += 1; + + var allocLen = trModel.allocated.length; + for (var i = allocLen - 1; i >= 0; i--) { + if (trModel.allocated[i].id === row.id) { + trModel.allocated.splice(i, 1); + } + } + }; + + // Show/hide allocated or available sections + model.toggleView = function(view) { + var show = model.views[view]; + model.views[view] = !show; + }; + + // Allocated array needs to be updated when rows re-ordered + model.updateAllocated = function(e, item, orderedItems) { + var allocLen = trModel.allocated.length; + trModel.allocated.splice(0, allocLen); + Array.prototype.push.apply(trModel.allocated, orderedItems); + }; + + model.numAllocated = function() { + return trModel.allocated ? trModel.allocated.length : 0; + }; + + model.numDisplayedAvailable = function() { + if (trModel.displayedAvailable) { + var filtered = trModel.displayedAvailable.filter(function(avail) { + return !model.allocatedIds[avail.id]; + }); + + return filtered.length; + } + return 0; + }; + }] + ) + + /** + * @ngdoc directive + * @name hz.widget.transfer-table.directive:transferTable + * @element + * @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). + * + * The data model requires 4 arrays: allocated, displayedAllocated, + * available, and displayedAvailable. Smart-Table requires 'displayed' + * arrays for sorting and re-ordering. + * + * Data model: + * ``` + * $scope.available = [ + * { 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.allocated = []; + * + * $scope.tableData = { + * available: $scope.available, + * displayedAvailable: [].concat($scope.available), + * allocated: $scope.allocated, + * displayedAllocated: [].concat($scope.allocated) + * }; + * + * $scope.helpText = { + * availHelpText: 'Select one from the list' + * }; + * + * $scope.limits = { + * maxAllocation: -1 + * }; + * ``` + * 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 + * + * @restrict E + * + * @example + * There are 2 examples available as a template: allocated.html.example and + * available.html.example. The `transferTableCtrl` methods are available + * via `trCtrl`. For example, for allocation, use `trCtrl.allocate`. + * ``` + * + * + * + * ... header definition ... + * + * + * + * ... more cell definitions + * + * + * + * + * + * + * + *
{$ row.username $} + * + * + * + * + * + *
+ * ... detail row definition ... + *
+ *
+ * + * + * ... header definition ... + * + * + * + * ... more cell definitions + * + * + * + * + * + * + *
{$ row.username $} + * + * + * + * + * + *
+ * ... detail row definition ... + *
+ *
+ *
+ * ``` + */ + .directive('transferTable', [ 'basePath', + function(path) { + return { + controller: 'transferTableCtrl', + controllerAs: 'trCtrl', + restrict: ' E', + scope: true, + transclude: true, + templateUrl: path + 'transfer-table/transfer-table.html', + link: function(scope, element, attrs, ctrl, transclude) { + var allocated = element.find('.transfer-allocated'); + var available = element.find('.transfer-available'); + + transclude(scope, function(clone) { + allocated.append(clone.filter('allocated')); + available.append(clone.filter('available')); + }); + } + }; + } + ]); + +})(); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/transfer-table/transfer-table.scss horizon-2015.1~b3/horizon/static/angular/transfer-table/transfer-table.scss --- horizon-2015.1~b2/horizon/static/angular/transfer-table/transfer-table.scss 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/transfer-table/transfer-table.scss 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,50 @@ +.transfer-table { + + .collapsed-help { + color: $transfer-help-text-color; + font-style: italic; + font-weight: 400; + margin-bottom: 3em; + } + + .fa[title] { + cursor: pointer; + } + + .transfer-heading { + border-bottom: $transfer-header-bottom-border; + font-size: 1.2em; + margin-top: 1em; + padding-bottom: 0.5em; + + .badge-info { + background-color: $badge-info-color; + } + + .help-text { + font-size: 0.9em; + font-weight: 400; + } + } + + .transfer-available, .transfer-allocated { + margin-bottom: 3em; + + table { + .action-col { + min-width: $transfer-btn-width; + width: $transfer-btn-width; + + .btn { + border-color: $transfer-btn-border-color; + padding: 2px 7px; + + &.disabled { + border-color: $transfer-disabled-btn-border-color; + color: $transfer-disabled-btn-color; + } + } + } + } + } +} \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/transfer-table/transfer-table.spec.js horizon-2015.1~b3/horizon/static/angular/transfer-table/transfer-table.spec.js --- horizon-2015.1~b2/horizon/static/angular/transfer-table/transfer-table.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/transfer-table/transfer-table.spec.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,197 @@ +/* jshint browser: true */ +(function() { + 'use strict'; + + describe('hz.widget.transfer-table module', function() { + it('should have been defined', function() { + expect(angular.module('hz.widget.transfer-table')).toBeDefined(); + }); + }); + + describe('transfer-table directive', function() { + + var $scope, $element; + + beforeEach(module('templates')); + beforeEach(module('smart-table')); + beforeEach(module('hz')); + beforeEach(module('hz.widgets')); + beforeEach(module('hz.widget.action-list')); + beforeEach(module('hz.widget.table')); + beforeEach(module('hz.widget.transfer-table')); + + describe('max 1 allocation', function() { + + beforeEach(inject(function($injector) { + var $compile = $injector.get('$compile'); + $scope = $injector.get('$rootScope').$new(); + + var available = [ + { id: '1', animal: 'cat' }, + { id: '2', animal: 'dog' }, + { id: '3', animal: 'fish' } + ]; + + $scope.tableData = { + available: available, + allocated: [], + displayedAvailable: [].concat(available), + displayedAllocated: [] + }; + + var markup = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Animal
{$ alRow.animal $}' + + ' x' + + ' ' + + '
' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Animal
{$ row.animal $}' + + ' x' + + ' ' + + '
' + + '
' + + '
'; + + $element = angular.element(markup); + $compile($element)($scope); + + $scope.$digest(); + })); + + it('should have 0 allocated rows', function() { + expect($element.find('.transfer-allocated tr[ng-repeat]').length).toBe(0); + }); + + it('should have 3 available rows', function() { + expect($element.find('.transfer-available tr[ng-repeat]').length).toBe(3); + }); + + it('should have 1 allocated row if first available row allocated', function() { + $element.find('.transfer-available tbody tr:first-child button').click(); + expect($element.find('.transfer-allocated tr[ng-repeat]').length).toBe(1); + }); + + it('should swap allocated row if one already exists', function(done) { + var available = $element.find('.transfer-available tbody tr:first-child button'); + available.click(); + + // After first click, should be one allocated row + var allocated = $element.find('.transfer-allocated tr[ng-repeat]'); + expect(allocated.length).toBe(1); + expect(allocated.find('td:nth-child(1)').text().trim()).toBe('cat'); + + // After second click, previously allocated row swapped out for new one + available = $element.find('.transfer-available tbody tr:first-child button'); + available.click(); + + setTimeout(function() { + console.log($element.html()); + allocated = $element.find('.transfer-allocated tr[ng-repeat]'); + expect(allocated.length).toBe(1); + expect(allocated.find('td:nth-child(1)').text().trim()).toBe('dog'); + done(); + }, 100); + }); + + it('should have 0 allocated row if allocated row de-allocated', function() { + $element.find('.transfer-allocated tr[ng-repeat] button').click(); + expect($element.find('.transfer-allocated tr[ng-repeat]').length).toBe(0); + expect($element.find('.transfer-available tr[ng-repeat]').length).toBe(3); + }); + + }); + + describe('max 2 allocations', function() { + + beforeEach(inject(function($injector) { + var $compile = $injector.get('$compile'); + $scope = $injector.get('$rootScope').$new(); + + var available = [ + { id: '1', animal: 'cat' }, + { id: '2', animal: 'dog' }, + { id: '3', animal: 'fish' } + ]; + + $scope.tableData = { + available: available, + allocated: [], + displayedAvailable: [].concat(available), + displayedAllocated: [] + }; + + $scope.limits = { + maxAllocation: 2 + }; + + var markup = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Animal
{$ row.animal $}' + + ' x' + + ' ' + + '
' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Animal
{$ alRow.animal $}' + + ' x' + + ' ' + + '
' + + '
' + + '
'; + + $element = angular.element(markup); + $compile($element)($scope); + + $scope.$digest(); + })); + + it('should have 0 allocated rows', function() { + expect($element.find('.transfer-allocated tr[ng-repeat]').length).toBe(0); + }); + + it('should have 3 available rows', function() { + expect($element.find('.transfer-available tr[ng-repeat]').length).toBe(3); + }); + + it('should allow only 2 allocated rows', function() { + $element.find('.transfer-available tbody tr:first-child button').click(); + $element.find('.transfer-available tbody tr:first-child button').click(); + + var lastRow = $element.find('.transfer-available tbody tr:first-child'); + lastRow.find('.action-col .btn').click(); + + expect($element.find('.transfer-allocated tr[ng-repeat]').length).toBe(2); + expect($element.find('.transfer-available tr[ng-repeat]').length).toBe(1); + + // The last row should not have been added + expect(lastRow.find('td:nth-child(1)').text().trim()).toBe('fish'); + }); + }); + }); +})(); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/angular/widget.module.js horizon-2015.1~b3/horizon/static/angular/widget.module.js --- horizon-2015.1~b2/horizon/static/angular/widget.module.js 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/widget.module.js 2015-03-19 19:07:29.000000000 +0000 @@ -2,7 +2,18 @@ 'use strict'; angular.module('hz.widgets', [ - 'hz.widget.help-panel' + 'hz.widget.form', + 'hz.widget.help-panel', + 'hz.widget.wizard', + 'hz.widget.table', + 'hz.widget.modal', + 'hz.widget.modal-wait-spinner', + 'hz.framework.bind-scope', + 'hz.widget.transfer-table', + 'hz.widget.charts', + 'hz.widget.action-list', + 'hz.widget.metadata-tree', + 'hz.widget.metadata-display' ]) .constant('basePath', '/static/angular/'); diff -Nru horizon-2015.1~b2/horizon/static/angular/wizard/wizard.html horizon-2015.1~b3/horizon/static/angular/wizard/wizard.html --- horizon-2015.1~b2/horizon/static/angular/wizard/wizard.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/wizard/wizard.html 2015-03-19 19:07:29.000000000 +0000 @@ -0,0 +1,66 @@ +
+
+ + + +
+ + +
+ +
+
+ +
+ +
+ + + + + + + +
+
+ +
+ + + + +
diff -Nru horizon-2015.1~b2/horizon/static/angular/wizard/wizard.js horizon-2015.1~b3/horizon/static/angular/wizard/wizard.js --- horizon-2015.1~b2/horizon/static/angular/wizard/wizard.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/wizard/wizard.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,49 @@ +(function () { + 'use strict'; + + angular.module('hz.widget.wizard', ['ui.bootstrap']) + + .constant('wizardLabels', { + cancel: gettext('Cancel'), + back: gettext('Back'), + next: gettext('Next'), + finish: gettext('Finish') + }) + + .directive('wizard', ['basePath', function (path) { + return { + controller: ['$scope', 'wizardLabels', function ($scope, wizardLabels) { + $scope.currentIndex = 0; + $scope.openHelp = false; + $scope.workflow = $scope.workflow || {}; + $scope.btnText = angular.extend({}, wizardLabels, $scope.workflow.btnText); + $scope.btnIcon = $scope.workflow.btnIcon || {}; + $scope.steps = $scope.workflow.steps || []; + $scope.wizardForm = {}; + $scope.showSpinner = false; + $scope.hasError = false; + $scope.switchTo = function (index) { + $scope.currentIndex = index; + $scope.openHelp = false; + }; + $scope.showError = function (errorMessage) { + $scope.showSpinner = false; + $scope.errorMessage = errorMessage; + $scope.hasError = true; + }; + }], + templateUrl: path + 'wizard/wizard.html' + }; + }]) + + .controller('ModalContainerCtrl', ['$scope', '$modalInstance', function ($scope, $modalInstance) { + $scope.close = function () { + $modalInstance.close(); + }; + $scope.cancel = function () { + $modalInstance.dismiss(); + }; + } + ]); + +})(); diff -Nru horizon-2015.1~b2/horizon/static/angular/wizard/wizard.scss horizon-2015.1~b3/horizon/static/angular/wizard/wizard.scss --- horizon-2015.1~b2/horizon/static/angular/wizard/wizard.scss 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/wizard/wizard.scss 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,264 @@ +.ng-wizard { + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: hidden; + font-weight: normal; + + .title { + height: $wizardTitleBarHeight; + line-height: $wizardTitleBarHeight - 6px; + vertical-align: bottom; + padding: 6px $WizardSidePadding 0 $WizardSidePadding; + color: #555; + border-bottom: 1px solid #ddd; + font-size: 22px; + font-weight: normal; + } + + & > .nav { + display: inline-block; + top: 40px; + left: 0px; + bottom: 40px; + width: $wizardNavWidth; + padding: 10px 10px 10px $WizardSidePadding; + + .nav-item { + position: relative; + display: block; + height: $WizardNavItemHeight; + width: $WizardNavItemWidth; + text-align: left; + padding-left: 10px; + background: transparent; + border: none; + border-bottom: 1px solid $WizardNavItemBdColor; + margin-top: -1px; + color: $WizardNavItemColor; + font-size: 12px; + border-radius: 0; + + &::after { + content: " "; + position: absolute; + top: 0; + right: -10px; + display: block; + height: inherit; + width: 0; + border-left: none; + border-top: $WizardNavItemHeight/2 solid transparent; + border-bottom: $WizardNavItemHeight/2 solid transparent; + } + + &[disabled] { + color: #444; + background: transparent; + } + + &.current { + width: $WizardNavItemWidth; + background: $WizardNavItemBgColor; + color: $WizardButtonColorHiLight; + border-color: $WizardNextBtnBgColor; + + &::after { + border-left: $WizardNavItemBgColor 10px solid; + } + + &:focus { + outline: none; + } + + .status-indicator { + color: inherit; + } + } + + .status-indicator { + position: absolute; + right: 0; + top: 18px; + width: $WizardStatusIndicatorSize; + height: $WizardStatusIndicatorSize; + color: orange; + } + } + } + + .step { + position: absolute; + top: $wizardTitleBarHeight; + left: $wizardNavWidth; + right: 0; + bottom: $wizardToolBarHeight; + color: #888; + + h1 { + position: absolute; + top: 0; + height: 45px; + left: 10px; + right: 65px; + margin: 0; + padding-top: 20px; + } + + h2.section-title { + margin-bottom: 5px; + padding-bottom: 5px; + } + + h1, h2.section-title { + font-size: 18px; + font-weight: normal; + color: #555; + border-bottom: 1px solid #ddd; + } + + .content { + position: absolute; + top: 45px; + left: 10px; + right: 0; + bottom: 0; + padding: 0 65px 24px 0; + overflow: auto; + + .subtitle { + margin-bottom: 30px; + } + + label { + font-weight: normal; + color: #555; + } + } + } + + .toolbar { + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: $wizardToolBarHeight; + line-height: $wizardToolBarHeight; + vertical-align: middle; + background: $WizardToolbarBgColor; + border-top: 1px solid $WizardBtnBdColor; + + .secondary-btn-grp { + position: absolute; + left: $WizardSidePadding; + } + + .primary-btn-grp { + position: absolute; + right: $WizardSidePadding; + } + + .secondary-btn-grp button { + margin-right: $WizardBtnGap - 3px; // white space character takes 3px + } + + .primary-btn-grp button { + margin-left: $WizardBtnGap - 3px; // white space character takes 3px + } + + .btn-wrap { + display: inline-block; + } + + .btn-wrap.finish { + padding-left: 15px; + margin-left: 20px; + border-left: 2px solid #ddd; + } + + .separator { + display: inline-block; + width: 0; + margin-left: 20px; + margin-right: 15px; + border-left: 1px solid $WizardToolbarVerticalSeparatorBdColor; + height: $wizardToolBarHeight; + vertical-align: top; + } + + button { + height: $WizardToolbarBtnHeight; + line-height: $WizardToolbarBtnHeight - 2px; + vertical-align: baseline; + padding: 0 25px 2px 25px; + font-size: 14px; + color: $WizardBtnTextColor; + border: 1px solid $WizardBtnBdColor; + background: $WizardBtnBgColor; + + &[disabled] { + color: #ccc; + border-color: #ddd; + background: #fff; + } + + &.next { + color: $WizardButtonColorHiLight; + border-color: $WizardNextBtnBdColor; + background: $WizardNextBtnBgColor; + + &[disabled] { + color: $WizardButtonColorHiLight; + border-color: $WizardFinishBtnDisabledBdColor; + background: $WizardFinishBtnDisabledBgColor; + } + } + + &.finish { + color: $WizardButtonColorHiLight; + border-color: $WizardFinishBtnBdColor; + background: $WizardFinishBtnBgColor; + + &[disabled] { + color: $WizardButtonColorHiLight; + border-color: $WizardFinishBtnDisabledBdColor; + background: $WizardFinishBtnDisabledBgColor; + } + } + } + } + + .help-panel { + top: $wizardTitleBarHeight; + bottom: $wizardToolBarHeight; + } + + .error-message { + display: none !important; + } +} + +.modal-dialog-wizard { + + .modal-dialog { + position: relative; + margin: 0 auto; + height: $wizardHeight; + width: $wizardWidth; + min-height: $wizardMinHeight; + min-width: $wizardMinWidth; + overflow-x: auto; + + .modal-content { + position: absolute; + top: $wizardTopPadding; + left: 0; + right: 0; + bottom: $wizardBottomPadding; + border-radius: 0; + } + } +} diff -Nru horizon-2015.1~b2/horizon/static/angular/wizard/wizard.spec.js horizon-2015.1~b3/horizon/static/angular/wizard/wizard.spec.js --- horizon-2015.1~b2/horizon/static/angular/wizard/wizard.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/angular/wizard/wizard.spec.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,174 @@ +/* jshint globalstrict: true */ +'use strict'; + +describe('hz.widget.wizard module', function () { + it('should have been defined".', function () { + expect(angular.module('hz.widget.wizard')).toBeDefined(); + }); +}); + +describe('wizard directive', function () { + var $compile, + $scope; + + beforeEach(module('templates')); + beforeEach(module('hz')); + beforeEach(module('hz.widgets')); + beforeEach(module('hz.widget.wizard')); + beforeEach(inject(function ($injector) { + $scope = $injector.get('$rootScope').$new(); + $compile = $injector.get('$compile'); + })); + + it('should be compiled', function () { + var element = $compile('some text')($scope); + $scope.$digest(); + expect(element.html().trim()).not.toBe('some text'); + }); + + it('should have empty title by default', function () { + var element = $compile('')($scope); + $scope.workflow = {}; + $scope.$digest(); + expect(element[0].querySelector('.title').textContent).toBe(''); + }); + + it('should have title if it is specified by workflow', function () { + var titleText = 'Some title'; + var element = $compile('')($scope); + $scope.workflow = {}; + $scope.workflow.title = titleText; + $scope.$digest(); + expect(element[0].querySelector('.title').textContent).toBe(titleText); + }); + + it('should have no steps if no steps defined', function () { + var element = $compile('')($scope); + $scope.workflow = {}; + $scope.$digest(); + expect(element[0].querySelectorAll('.step').length).toBe(0); + }); + + it('should have 3 steps if 3 steps defined', function () { + var element = $compile('')($scope); + $scope.workflow = { + steps: [ {}, {}, {} ] + }; + $scope.$digest(); + expect(element[0].querySelectorAll('.step').length).toBe(3); + }); + + it('should have no nav items if no steps defined', function () { + var element = $compile('')($scope); + $scope.workflow = {}; + $scope.$digest(); + expect(element[0].querySelectorAll('.nav-item').length).toBe(0); + }); + + it('should have 3 nav items if 3 steps defined', function () { + var element = $compile('')($scope); + $scope.workflow = { + steps: [ {}, {}, {} ] + }; + $scope.$digest(); + expect(element[0].querySelectorAll('.nav-item').length).toBe(3); + }); + + it('should navigate correctly', function () { + var element = $compile('')($scope); + $scope.workflow = { + steps: [ {}, {}, {} ] + }; + + $scope.$digest(); + expect($scope.currentIndex).toBe(0); + expect($(element).find('.step').eq(0).hasClass('ng-hide')).toBe(false); + expect($(element).find('.step').eq(1).hasClass('ng-hide')).toBe(true); + expect($(element).find('.step').eq(2).hasClass('ng-hide')).toBe(true); + expect($(element).find('.nav-item').eq(0).hasClass('current')).toBe(true); + expect($(element).find('.nav-item').eq(1).hasClass('current')).toBe(false); + expect($(element).find('.nav-item').eq(2).hasClass('current')).toBe(false); + + $scope.switchTo(1); + $scope.$digest(); + expect($scope.currentIndex).toBe(1); + expect($(element).find('.step').eq(0).hasClass('ng-hide')).toBe(true); + expect($(element).find('.step').eq(1).hasClass('ng-hide')).toBe(false); + expect($(element).find('.step').eq(2).hasClass('ng-hide')).toBe(true); + expect($(element).find('.nav-item').eq(0).hasClass('current')).toBe(false); + expect($(element).find('.nav-item').eq(1).hasClass('current')).toBe(true); + expect($(element).find('.nav-item').eq(2).hasClass('current')).toBe(false); + + $scope.switchTo(2); + $scope.$digest(); + expect($scope.currentIndex).toBe(2); + expect($(element).find('.step').eq(0).hasClass('ng-hide')).toBe(true); + expect($(element).find('.step').eq(1).hasClass('ng-hide')).toBe(true); + expect($(element).find('.step').eq(2).hasClass('ng-hide')).toBe(false); + expect($(element).find('.nav-item').eq(0).hasClass('current')).toBe(false); + expect($(element).find('.nav-item').eq(1).hasClass('current')).toBe(false); + expect($(element).find('.nav-item').eq(2).hasClass('current')).toBe(true); + }); + + it('should not show back button in step 1/3', function () { + var element = $compile('')($scope); + $scope.workflow = { + steps: [{}, {}, {}] + }; + $scope.$digest(); + expect($(element).find('button.back').hasClass('ng-hide')).toBe(true); + expect($(element).find('button.next').hasClass('ng-hide')).toBe(false); + }); + + it('should show both back and next button in step 2/3', function () { + var element = $compile('')($scope); + $scope.workflow = { + steps: [{}, {}, {}] + }; + $scope.$digest(); + $scope.switchTo(1); + $scope.$digest(); + expect($(element).find('button.back').hasClass('ng-hide')).toBe(false); + expect($(element).find('button.next').hasClass('ng-hide')).toBe(false); + }); + + it('should not show next button in step 3/3', function () { + var element = $compile('')($scope); + $scope.workflow = { + steps: [{}, {}, {}] + }; + $scope.$digest(); + $scope.switchTo(2); + $scope.$digest(); + expect($(element).find('button.back').hasClass('ng-hide')).toBe(false); + expect($(element).find('button.next').hasClass('ng-hide')).toBe(true); + }); + + it('should have finish button disabled if wizardForm is invalid', function () { + var element = $compile('')($scope); + $scope.wizardForm = { }; + $scope.$digest(); + $scope.wizardForm.$invalid = true; + $scope.$digest(); + expect(element[0].querySelector('button.finish').hasAttribute('disabled')).toBe(true); + }); + + it('should have finish button enabled if wizardForm is valid', function () { + var element = $compile('')($scope); + $scope.wizardForm = { }; + $scope.$digest(); + $scope.wizardForm.$invalid = false; + $scope.$digest(); + expect(element[0].querySelector('button.finish').hasAttribute('disabled')).toBe(false); + }); + + it('should show error message after calling method showError', function () { + var errorMessage = 'some error message'; + var element = $compile('')($scope); + $scope.$digest(); + $scope.showError(errorMessage); + $scope.$digest(); + expect(element[0].querySelector('.error-message').textContent).toBe(errorMessage); + }); + +}); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/controllers/metadata-widget-controller.js horizon-2015.1~b3/horizon/static/horizon/js/angular/controllers/metadata-widget-controller.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/controllers/metadata-widget-controller.js 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/controllers/metadata-widget-controller.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,329 +0,0 @@ -(function () { - 'use strict'; - angular.module('hz') - .controller('hzMetadataWidgetCtrl', ['$scope', '$window', '$filter', function ($scope, $window, $filter) { - - //// Item class //// - - function Item(parent) { - // parent as property to prevent infinite recursion in angular filter - Object.defineProperty(this, 'parent', { - value: typeof parent !== 'undefined' ? parent : null - }); - this.children = []; - // Node properties - this.visible = false; - this.expanded = false; - this.label = ''; - this.description = ''; - this.level = parent ? parent.level + 1 : 0; - this.addedCount = 0; - this.custom = false; - // Leaf properties - this.leaf = null; - this.added = false; - } - - Item.prototype.fromNamespace = function(namespace) { - this.label = namespace.display_name; - this.description = namespace.description; - - if(namespace.objects) { - angular.forEach(namespace.objects, function(object) { - this.children.push(new Item(this).fromObject(object)); - }, this); - } - - if(namespace.properties){ - angular.forEach(namespace.properties, function(property, key) { - this.children.push(new Item(this).fromProperty(key, property)); - }, this); - } - - this.sortChildren(); - - return this; - }; - - Item.prototype.fromObject = function(object) { - this.label = object.name; - this.description = object.description; - - if(object.properties) { - angular.forEach(object.properties, function (property, key) { - this.children.push(new Item(this).fromProperty(key, property)); - }, this); - } - - this.sortChildren(); - - return this; - }; - - Item.prototype.fromProperty = function(name, property) { - this.leaf = property || {}; - this.label = this.leaf.title || ''; - this.description = this.leaf.description || ''; - this.leaf.name = name; - this.setLeafValue(this.leaf.default || null); - - return this; - }; - - Item.prototype.customProperty = function(name) { - this.fromProperty(name, {title: name}); - this.leaf.type = 'string'; - this.custom = true; - - return this; - }; - - Item.prototype.expand = function() { - this.expanded = true; - angular.forEach(this.children, function(child) { - child.visible = true; - }, this); - }; - - Item.prototype.collapse = function() { - this.expanded = false; - angular.forEach(this.children, function(child) { - child.collapse(); - child.visible = false; - }, this); - }; - - Item.prototype.sortChildren = function() { - this.children.sort(function(a, b) { - return a.label.localeCompare(b.label); - }); - }; - - Item.prototype.markAsAdded = function() { - this.added = true; - if(this.parent) { - this.parent.addedCount += 1; - if(this.parent.addedCount === this.parent.children.length) { - this.parent.added = true; - } - } - angular.forEach(this.children, function(item) { - item.markAsAdded(); - }, this); - }; - - Item.prototype.unmarkAsAdded = function(caller) { - this.added = false; - if(this.parent) { - this.parent.addedCount -= 1; - this.parent.expand(); - this.parent.unmarkAsAdded(this); - } - if(!caller) { // prevent infinite recursion - angular.forEach(this.children, function(item) { - item.unmarkAsAdded(); - }, this); - } - }; - - Item.prototype.path = function(path) { - path = typeof path !== 'undefined' ? path : []; - if(this.parent) this.parent.path(path); - path.push(this.label); - return path; - }; - - Item.prototype.setLeafValue = function(value) { - if(value === null) { - this.leaf.value = null; - return; - } - - switch (this.leaf.type) { - case 'integer': this.leaf.value = parseInt(value); break; - case 'number': this.leaf.value = parseFloat(value); break; - case 'array': this.leaf.value = value.replace(/^ /, ''); break; - case 'boolean': this.leaf.value = parseBool(value); break; - default: this.leaf.value = value; - } - }; - - //// Private functions //// - - var filter = $filter('filter'); - - function loadNamespaces(namespaces) { - var items = []; - - angular.forEach(namespaces, function(namespace) { - var item = new Item().fromNamespace(namespace); - item.visible = true; - items.push(item); - }); - - items.sort(function(a, b) { - return a.label.localeCompare(b.label); - }); - - return items; - } - - function flattenTree(tree, items) { - items = typeof items !== 'undefined' ? items : []; - - angular.forEach(tree, function(item) { - items.push(item); - flattenTree(item.children, items); - }); - - return items; - } - - function loadExisting(available, existing) { - var itemsMapping = {}; - - angular.forEach(available, function(item) { - if(item.leaf && item.leaf.name in existing) { - itemsMapping[item.leaf.name] = item; - } - }); - - angular.forEach(existing, function(value, key) { - var item = itemsMapping[key]; - if(typeof item === 'undefined') { - item = new Item().customProperty(key); - available.push(item); - } - item.setLeafValue(value); - item.markAsAdded(); - }); - } - - function parseBool(value) { - var value_type = typeof(value); - - if(value_type === 'boolean') { - return value; - } - else if(value_type === 'string') { - value = value.toLowerCase(); - - if(value === 'true') { - return true; - } - else if(value === 'false') { - return false; - } - } - - return null; - } - - //// Public functions //// - - $scope.onItemClick = function(e, item) { - $scope.selected = item; - if(!item.expanded) { - item.expand(); - } else { - item.collapse(); - } - }; - - $scope.onItemAdd = function(e, item) { - $scope.selected = item; - item.markAsAdded(); - }; - - $scope.onItemDelete = function(e, item) { - if(!item.custom) { - $scope.selected = item; - item.unmarkAsAdded(); - } else { - $scope.selected = null; - var i = $scope.flatTree.indexOf(item); - if(i > -1) { - $scope.flatTree.splice(i, 1); - } - } - }; - - $scope.onCustomItemAdd = function(e) { - var item, name = $scope.customItem.value; - if($scope.customItem.found.length > 0) { - item = $scope.customItem.found[0]; - item.markAsAdded(); - $scope.selected = item; - } else { - item = new Item().customProperty(name); - item.markAsAdded(); - $scope.selected = item; - $scope.flatTree.push(item); - } - $scope.customItem.valid = false; - $scope.customItem.value = ''; - }; - - $scope.formatErrorMessage = function(item, error) { - var _ = $window.gettext; - if(error.min) return _('Min') + ' ' + item.leaf.minimum; - if(error.max) return _('Max') + ' ' + item.leaf.maximum; - if(error.minlength) return _('Min length') + ' ' + item.leaf.minLength; - if(error.maxlength) return _('Max length') + ' ' + item.leaf.maxLength; - if(error.pattern) { - if(item.leaf.type === 'integer') return _('Integer required'); - else return _('Pattern mismatch'); - } - if(error.required) { - switch(item.leaf.type) { - case 'integer': return _('Integer required'); - case 'number': return _('Decimal required'); - default: return _('Required'); - } - } - }; - - $scope.saveMetadata = function () { - var metadata = []; - var added = filter($scope.flatTree, {'added': true, 'leaf': '!null'}); - angular.forEach(added, function(item) { - metadata.push({ - key: item.leaf.name, - value: (item.leaf.type == 'array' ? ' ' : '') + item.leaf.value - }); - }); - $scope.metadata = JSON.stringify(metadata); - }; - - $scope.$watch('customItem.value', function() { - $scope.customItem.found = filter( - $scope.flatTree, {'leaf.name': $scope.customItem.value}, true - ); - $scope.customItem.valid = $scope.customItem.value && - $scope.customItem.found.length === 0; - }); - - //// Private variables //// - - var tree = loadNamespaces($window.available_metadata.namespaces); - - //// Public variables //// - - $scope.flatTree = flattenTree(tree); - $scope.decriptionText = ''; - $scope.metadata = ''; - $scope.selected = null; - $scope.customItem = { - value: '', - focused: false, - valid: false, - found: [] - }; - $scope.filterText = { - available: '', - existing: '' - }; - loadExisting($scope.flatTree, $window.existing_metadata); - - }]); -}()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/controllers/modal-form-update-metadata-ctrl.js horizon-2015.1~b3/horizon/static/horizon/js/angular/controllers/modal-form-update-metadata-ctrl.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/controllers/modal-form-update-metadata-ctrl.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/controllers/modal-form-update-metadata-ctrl.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,23 @@ +(function () { + 'use strict'; + angular.module('hz') + .controller('hzModalFormUpdateMetadataCtrl', [ + '$scope', '$window', + function ($scope, $window) { + $scope.tree = null; + $scope.available = $window.available_metadata.namespaces; + $scope.existing = $window.existing_metadata; + + $scope.saveMetadata = function () { + var metadata = []; + angular.forEach($scope.tree.getExisting(), function (value, key) { + metadata.push({ + key: key, + value: value + }); + }); + $scope.metadata = JSON.stringify(metadata); + }; + } + ]); +}()); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/directives/serialConsole.js horizon-2015.1~b3/horizon/static/horizon/js/angular/directives/serialConsole.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/directives/serialConsole.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/directives/serialConsole.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,95 @@ +/* +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. +*/ + +/*global Terminal,Blob,FileReader,gettext,interpolate */ +(function() { + 'use strict'; + + angular.module('serialConsoleApp', []) + .constant('protocols', ['binary', 'base64']) + .constant('states', [gettext('Connecting'), gettext('Open'), gettext('Closing'), gettext('Closed')]) + + /** + * @ngdoc directive + * @ngname serialConsole + * + * @description + * The serial-console element creates a terminal based on the widely-used term.js. + * The "connection" attribute is input to a WebSocket object, which connects + * to a server. In Horizon, this directive is used to connect to nova-serialproxy, + * opening a serial console to any instance. Each key the user types is transmitted + * to the instance, and each character the instance reponds with is displayed. + */ + .directive('serialConsole', function(protocols, states) { + return { + scope: true, + template: '

{{statusMessage()}}', + restrict: 'E', + link: function postLink(scope, element, attrs) { + + var connection = scope.$eval(attrs.connection); + var term = new Terminal(); + var socket = new WebSocket(connection, protocols); + + socket.onerror = function() { + scope.$apply(scope.status); + }; + socket.onopen = function() { + scope.$apply(scope.status); + // initialize by "hitting enter" + socket.send(String.fromCharCode(13)); + }; + socket.onclose = function() { + scope.$apply(scope.status); + }; + + // turn the angular jQlite element into a raw DOM element so we can + // attach the Terminal to it + var termElement = angular.element(element)[0]; + term.open(termElement.ownerDocument.getElementById('terminalNode')); + + term.on('data', function(data) { + socket.send(data); + }); + + socket.onmessage = function(e) { + if (e.data instanceof Blob) { + var f = new FileReader(); + f.onload = function() { + term.write(f.result); + }; + f.readAsText(e.data); + } else { + term.write(e.data); + } + }; + + scope.status = function() { + return states[socket.readyState]; + }; + + scope.statusMessage = function() { + return interpolate(gettext('Status: %s'), [scope.status()]); + }; + + scope.$on('$destroy', function() { + socket.close(); + }); + + } + }; + }); +}()); \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/filters/filters.js horizon-2015.1~b3/horizon/static/horizon/js/angular/filters/filters.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/filters/filters.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/filters/filters.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,152 @@ +/* + * (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 hz.filters + * @description + * hz.filters provides common filters to be used within Horizon. + * + */ + angular.module('hz.filters', []) + + /** + * @ngdoc filter + * @name yesno + * @description + * Evaluates given input as boolean and returns translation + * of 'Yes' and 'No' for true/false respectively. + */ + .filter('yesno', function() { + 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. + */ + .filter('gb', function() { + return function(input) { + if (isNaN(input) || null === input) { + return ''; + } else { + return input.toString() + " " + gettext("GB"); + } + }; + }) + + /** + * @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. + */ + .filter('mb', function() { + return function(input) { + if (isNaN(input) || null === input) { + return ''; + } else { + return input.toString() + " " + gettext("MB"); + } + }; + }) + + /** + * @ngdoc filter + * @name title + * @description + * Capitalizes leading characters of individual words. + */ + .filter('title', function() { + return function(input) { + if (typeof input !== 'string') { + return input; + } + return input.replace(/(?:^|\s)\S/g, function(a) { + return a.toUpperCase(); + }); + }; + }) + + /** + * @ngdoc filter + * @name noUnderscore + * @description + * Replaces all underscores with spaces. + */ + .filter('noUnderscore', function() { + return function(input) { + if (typeof input !== 'string') { + return input; + } + return input.replace(/_/g, ' '); + }; + }) + + /** + * @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. + */ + .filter('decode', function() { + 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. + */ + .filter('bytes', function() { + 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 Number(input/tb).toFixed(2) + " " + gettext("TB"); + } else if (input >= gb) { + return Number(input/gb).toFixed(2) + " " + gettext("GB"); + } else if (input >= mb) { + return Number(input/mb).toFixed(2) + " " + gettext("MB"); + } else if (input >= kb) { + return Number(input/kb).toFixed(2) + " " + gettext("KB"); + } else { + return Math.floor(input) + " " + gettext("bytes"); + } + }; + }) + + ; +}()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/filters/filters.spec.js horizon-2015.1~b3/horizon/static/horizon/js/angular/filters/filters.spec.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/filters/filters.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/filters/filters.spec.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,124 @@ +describe('hz.filters', function () { + 'use strict'; + + beforeEach(module('hz.filters')); + + describe('yesno', function() { + + it('returns Yes for true', inject(function(yesnoFilter) { + expect(yesnoFilter(true)).toBe('Yes'); + })); + + it('returns No for false', inject(function(yesnoFilter) { + expect(yesnoFilter(false)).toBe('No'); + })); + + it('returns No for null', inject(function(yesnoFilter) { + expect(yesnoFilter(null)).toBe('No'); + })); + + }); + + describe('gb', function() { + + it('returns given numeric value properly', inject(function(gbFilter) { + expect(gbFilter(12)).toBe('12 GB'); + expect(gbFilter(-12)).toBe('-12 GB'); + expect(gbFilter(12.12)).toBe('12.12 GB'); + })); + + it('returns empty string for non-numeric', inject(function(gbFilter) { + expect(gbFilter('humbug')).toBe(''); + })); + + it('returns empty string for null', inject(function(gbFilter) { + expect(gbFilter(null)).toBe(''); + })); + + }); + + describe('mb', function() { + + it('returns given numeric value properly', inject(function(mbFilter) { + expect(mbFilter(12)).toBe('12 MB'); + expect(mbFilter(-12)).toBe('-12 MB'); + expect(mbFilter(12.12)).toBe('12.12 MB'); + })); + + it('returns empty string for non-numeric', inject(function(mbFilter) { + expect(mbFilter('humbug')).toBe(''); + })); + + it('returns empty string for null', inject(function(mbFilter) { + expect(mbFilter(null)).toBe(''); + })); + + }); + + describe('title', function() { + + it('capitalizes as expected', inject(function(titleFilter) { + expect(titleFilter('title')).toBe('Title'); + expect(titleFilter('we have several words')).toBe('We Have Several Words'); + })); + + }); + + describe('noUnderscore', function() { + + it('replaces all underscores with spaces', inject(function(noUnderscoreFilter) { + expect(noUnderscoreFilter('_this_is___a_lot____of_underscores__')).toBe(' this is a lot of underscores '); + })); + + it('returns falsy input', inject(function(noUnderscoreFilter) { + expect(noUnderscoreFilter(null)).toBe(null); + expect(noUnderscoreFilter(false)).toBe(false); + expect(noUnderscoreFilter('')).toBe(''); + })); + + }); + + describe("decode", function() { + + it("Returns value when key is present", inject(function(decodeFilter) { + expect(decodeFilter('PRESENT', {'PRESENT': 'Here'})).toBe('Here'); + })); + + it("Returns value when key is present and value is falsy", inject(function(decodeFilter) { + expect(decodeFilter('PRESENT', {'PRESENT': false})).toBe(false); + })); + + it("Returns input when key is not present", inject(function(decodeFilter) { + expect(decodeFilter('NOT_PRESENT', {'PRESENT': 'Here'})).toBe('NOT_PRESENT'); + })); + + }); + + describe('bytes', function() { + + it('returns TB values', inject(function(bytesFilter) { + expect(bytesFilter(1099511627776)).toBe('1.00 TB'); + })); + + it('returns GB values', inject(function(bytesFilter) { + expect(bytesFilter(1073741824)).toBe('1.00 GB'); + })); + + it('returns MB values', inject(function(bytesFilter) { + expect(bytesFilter(1048576)).toBe('1.00 MB'); + })); + + it('returns KB values', inject(function(bytesFilter) { + expect(bytesFilter(1024)).toBe('1.00 KB'); + })); + + it('returns byte values', inject(function(bytesFilter) { + expect(bytesFilter(0)).toBe('0 bytes'); + expect(bytesFilter(1)).toBe('1 bytes'); + expect(bytesFilter(1023)).toBe('1023 bytes'); + })); + + }); + + +}); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/horizon.js horizon-2015.1~b3/horizon/static/horizon/js/angular/horizon.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/horizon.js 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/horizon.js 2015-03-19 19:07:23.000000000 +0000 @@ -2,7 +2,7 @@ (function () { 'use strict'; - var horizon_dependencies = ['hz.conf', 'hz.utils', 'hz.api', 'ngCookies', 'hz.widgets']; + var horizon_dependencies = ['hz.conf', 'hz.utils', 'hz.api', 'ngCookies', 'hz.widgets', 'hz.filters']; var dependencies = horizon_dependencies.concat(angularModuleExtension); angular.module('hz', dependencies) .config(['$interpolateProvider', '$httpProvider', @@ -28,5 +28,8 @@ $cookieStore.put(key, value); }); }; + horizon.cookies.getRaw = function (key) { + return $cookies[key]; + }; }]); }()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.cinder.js horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.cinder.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.cinder.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.cinder.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,83 @@ +/* +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 hz.api.cinderAPI + * @description Provides direct access to Cinder APIs. + */ + function CinderAPI(apiService) { + + // Volumes + + /** + * @name hz.api.cinderAPI.getVolumes + * @description + * Get a list of volumes. + * + * The listing result is an object with property "items." Each item is + * a volume. + * + * @param {Object} params + * Query parameters. Optional. + * + * @param {string} param.search_opts + * Filters to pass through the API. + * For example, "status": "available" will show all available volumes. + */ + this.getVolumes = function(params) { + var config = (params) ? {'params': params} : {}; + return apiService.get('/api/cinder/volumes/', config) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve volumes.')); + }); + }; + + // Volume Snapshots + + /** + * @name hz.api.cinderAPI.getVolumeSnapshots + * @description + * Get a list of volume snapshots. + * + * The listing result is an object with property "items." Each item is + * a volume snapshot. + * + * @param {Object} params + * Query parameters. Optional. + * + * @param {string} param.search_opts + * Filters to pass through the API. + * For example, "status": "available" will show all available volume + * snapshots. + */ + this.getVolumeSnapshots = function(params) { + var config = (params) ? {'params': params} : {}; + return apiService.get('/api/cinder/volumesnapshots/', config) + .error(function () { + horizon.alert('error', + gettext('Unable to retrieve volume snapshots.')); + }); + }; + } + + // Register it with the API module so that anybody using the + // API module will have access to the Cinder APIs. + angular.module('hz.api') + .service('cinderAPI', ['apiService', CinderAPI]); +}()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.config.js horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.config.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.config.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.config.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,70 @@ +/* +Copyright 2015, 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'; + + /** + * @ngdoc service + * @name hz.api.configAPI + * @description Provides access to dashboard configuration. + */ + function ConfigAPI(apiService) { + + /** + * @name hz.api.configAPI.getUserDefaults + * @description + * Get the default user configuration settings. + * + * Returns an object with user configuration settings. + */ + this.getUserDefaults = function() { + return apiService.get('/api/config/user/') + .success(function(data) { + // store config in localStorage + // should be call only when defaults are needed + // or when user wants to reset it + localStorage.user_config = angular.toJson(data); + }) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve user configuration.')); + }); + }; + + /** + * @name hz.api.configAPI.getAdminDefaults + * @description + * Get the default admin configuration settings. + * + * Returns an object with admin configuration settings. + */ + this.getAdminDefaults = function(params) { + return apiService.get('/api/config/admin/') + .success(function(data) { + // store this in localStorage + // should be call once each page load + localStorage.admin_config = angular.toJson(data); + }) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve admin configuration.')); + }); + }; + } + + // Register it with the API module so that anybody using the + // API module will have access to the Config APIs. + angular.module('hz.api') + .service('configAPI', ['apiService', ConfigAPI]); +}()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.glance.js horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.glance.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.glance.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.glance.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,159 @@ +/* +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 service + * @name hz.api.glanceAPI + * @description Provides direct pass through to Glance with NO abstraction. + */ + function GlanceAPI(apiService) { + + // Images + + /** + * @name hz.api.glanceAPI.getImage + * @description + * Get a single image by ID + * @param {string} id + * Specifies the id of the image to request. + */ + this.getImage = function(id) { + return apiService.get('/api/glance/images/' + id) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve image.')); + }); + }; + + + /** + * @name hz.api.glanceAPI.getImages + * @description + * Get a list of images. + * + * The listing result is an object with property "items". Each item is + * an image. + * + * @param {Object} params + * Query parameters. Optional. + * + * @param {boolean} params.paginate + * True to paginate automatically. + * + * @param {string} params.marker + * Specifies the image of the last-seen image. + * + * The typical pattern of limit and marker is to make an + * initial limited request and then to use the last + * image from the response as the marker parameter + * in a subsequent limited request. With paginate, limit + * is automatically set. + * + * @param {string} params.sort_dir + * The sort direction ('asc' or 'desc'). + * + * @param {string} params.sort_key + * The field to sort on (for example, 'created_at'). + * Default is created_at. + * + * @param {string} params.other + * Any additional request parameters will be passed through the API as + * filters. For example "name" : "fedora" would filter on the fedora name. + */ + this.getImages = function(params) { + var config = (params) ? { 'params' : params} : {}; + return apiService.get('/api/glance/images/', config) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve images.')); + }); + }; + + // Metadata Definitions - Namespaces + + /** + * @name hz.api.glanceAPI.getNamespaces + * @description + * Get a list of metadata definition namespaces. + * + * http://docs.openstack.org/developer/glance/metadefs-concepts.html + * + * The listing result is an object with property "items". Each item is + * an namespace. + * + * @description + * Get a list of namespaces. + * + * The listing result is an object with property "items". Each item is + * a namespace. + * + * @param {Object} params + * Query parameters. Optional. + * + * @param {boolean} params.paginate + * True to paginate automatically. + * + * @param {string} params.marker + * Specifies the namespace of the last-seen namespace. + * + * The typical pattern of limit and marker is to make an + * initial limited request and then to use the last + * namespace from the response as the marker parameter + * in a subsequent limited request. With paginate, limit + * is automatically set. + * + * @param {string} params.sort_dir + * The sort direction ('asc' or 'desc'). + * + * @param {string} params.sort_key + * The field to sort on (for example, 'created_at'). + * Default is namespace. + * + * @param {string} params.other + * Any additional request parameters will be passed through the API as + * filters. + */ + this.getNamespaces = function(params) { + var config = (params) ? { 'params' : params} : {}; + return apiService.get('/api/glance/metadefs/namespaces/', config) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve namespaces.')); + }); + }; + + /** + * @name hz.api.glanceAPI.getImages + * @description + * Get a specific namespace. + * + * http://docs.openstack.org/developer/glance/metadefs-concepts.html + */ + this.getNamespace = function(namespace) { + return apiService.get('/api/glance/metadefs/namespaces/' + namespace) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve namespace.')); + }); + }; + + } + + // Register it with the API module so that anybody using the + // API module will have access to the Glance APIs. + + angular.module('hz.api') + .service('glanceAPI', ['apiService', GlanceAPI]); + +}()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.keystone.js horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.keystone.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.keystone.js 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.keystone.js 2015-03-19 19:07:23.000000000 +0000 @@ -13,13 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -/*global angular,horizon*/ (function () { 'use strict'; function KeystoneAPI(apiService) { // Users - this.getUsers = function() { - return apiService.get('/api/keystone/users/') + this.getUsers = function(params) { + var config = (params) ? {'params': params} : {}; + return apiService.get('/api/keystone/users/', config) .error(function () { horizon.alert('error', gettext('Unable to retrieve users')); }); @@ -150,8 +150,9 @@ }; // Projects - this.getProjects = function() { - return apiService.get('/api/keystone/projects/') + this.getProjects = function(params) { + var config = (params) ? {'params': params} : {}; + return apiService.get('/api/keystone/projects/', config) .error(function () { horizon.alert('error', gettext('Unable to retrieve projects')); }); @@ -200,8 +201,78 @@ horizon.alert('error', gettext('Unable to grant the role.')); }); }; + + /** + * @name hz.api.keyStoneAPI.serviceCatalog + * @description + * Returns the service catalog. + * @param {Object} config + * See $http config object parameters. + */ + this.serviceCatalog = function(config) { + return apiService.get('/api/keystone/svc-catalog/', config) + .error(function () { + horizon.alert('error', gettext('Unable to fetch the service catalog.')); + }); + }; } angular.module('hz.api') .service('keystoneAPI', ['apiService', KeystoneAPI]); + + + /** + * @ngdoc service + * @name hz.api.serviceCatalog + * @description + * Provides cached access to the Service Catalog with utilities to help + * with asynchronous data loading. The cache may be reset at any time + * by accessing the cache and calling removeAll. The next call to any + * function will retrieve fresh results. + * + * The enabled extensions do not change often, so using cached data will + * speed up results. Even on a local devstack in informal testing, + * this saved between 30 - 100 ms per request. + */ + function ServiceCatalog($cacheFactory, $q, keystoneAPI) { + + var service = {}; + service.cache = $cacheFactory('hz.api.serviceCatalog', {capacity: 1}); + + service.get = function() { + return keystoneAPI.serviceCatalog({cache: service.cache}) + .then(function(data){ + return data.data; + } + ); + }; + + service.ifTypeEnabled = function(desired, doThis) { + return service.get().then(function(result){ + if (enabled(result, 'type', desired)){ + return $q.when(doThis()); + } + } + ); + }; + + function enabled(resources, key, desired) { + if(resources) { + return resources.some(function (resource) { + return resource[key] === desired; + }); + } else { + return false; + } + } + + return service; + } + + angular.module('hz.api') + .factory('serviceCatalog', ['$cacheFactory', + '$q', + 'keystoneAPI', + ServiceCatalog]); + }()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.neutron.js horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.neutron.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.neutron.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.neutron.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,200 @@ +/** + * 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 hz.api.NeutronAPI + * @description Provides access to Neutron APIs. + */ + function NeutronAPI(apiService) { + + // Networks + + /** + * @name hz.api.neturonAPI.getNetworks + * @description + * Get a list of networks for a tenant. + * + * The listing result is an object with property "items". Each item is + * a network. + */ + this.getNetworks = function() { + return apiService.get('/api/neutron/networks/') + .error(function () { + horizon.alert('error', gettext('Unable to retrieve networks.')); + }); + }; + + /** + * @name hz.api.neutronAPI.createNetwork + * @description + * Create a new network. + * @returns The new network object on success. + * + * @param {Object} newNetwork + * The network to create. Required. + * + * Example new network object + * { + * "name": "myNewNetwork", + * "admin_state_up": true, + * "net_profile_id" : "asdsarafssdaser", + * "shared": true, + * "tenant_id": "4fd44f30292945e481c7b8a0c8908869 + * } + * + * Description of properties on the network object + * + * @property {string} newNetwork.name + * The name of the new network. Optional. + * + * @property {boolean} newNetwork.admin_state_up + * The administrative state of the network, which is up (true) or + * down (false). Optional. + * + * @property {string} newNetwork.net_profile_id + * The network profile id. Optional. + * + * @property {boolean} newNetwork.shared + * Indicates whether this network is shared across all tenants. + * By default, only adminstative users can change this value. Optional. + * + * @property {string} newNetwork.tenant_id + * The UUID of the tenant that will own the network. This tenant can + * be different from the tenant that makes the create network request. + * However, only administative users can specify a tenant ID other than + * their own. You cannot change this value through authorization + * policies. Optional. + * + */ + this.createNetwork = function(newNetwork) { + return apiService.post('/api/neutron/networks/', newNetwork) + .error(function () { + horizon.alert('error', gettext('Unable to create the network.')); + }); + }; + + // Subnets + + /** + * @name hz.api.neutronAPI.getSubnets + * @description + * Get a list of subnets for a network. + * + * The listing result is an object with property "items". Each item is + * a subnet. + * + * @param {string} network_id + * The network id to retrieve subnets for. Required. + */ + this.getSubnets = function(network_id) { + return apiService.get('/api/neutron/subnets/', network_id) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve subnets.')); + }); + }; + + /** + * @name hz.api.neutronAPI.createSubnet + * @description + * Create a Subnet for given Network. + * @returns The JSON representation of Subnet on success. + * + * @param {Object} newSubnet + * The subnet to create. + * + * Example new subnet object + * { + * "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + * "ip_version": 4, + * "cidr": "192.168.199.0/24", + * "name": "mySubnet", + * "tenant_id": "4fd44f30292945e481c7b8a0c8908869, + * "allocation_pools": [ + * { + * "start": "192.168.199.2", + * "end": "192.168.199.254" + * } + * ], + * "gateway_ip": "192.168.199.1", + * "id": "abce", + * "enable_dhcp": true, + * } + * + * Description of properties on the subnet object + * @property {string} newSubnet.network_id + * The id of the attached network. Required. + * + * @property {number} newSubnet.ip_version + * The IP version, which is 4 or 6. Required. + * + * @property {string} newSubnet.cidr + * The CIDR. Required. + * + * @property {string} newSubnet.name + * The name of the new subnet. Optional. + * + * @property {string} newSubnet.tenant_id + * The ID of the tenant who owns the network. Only administrative users + * can specify a tenant ID other than their own. Optional. + * + * @property {string|Array} newSubnet.allocation_pools + * The start and end addresses for the allocation pools. Optional. + * + * @property {string} newSubnet.gateway_ip + * The gateway IP address. Optional. + * + * @property {string} newSubnet.id + * The ID of the subnet. Optional. + * + * @property {boolean} newSubnet.enable_dhcp + * Set to true if DHCP is enabled and false if DHCP is disabled. Optional. + * + */ + this.createSubnet = function(newSubnet) { + return apiService.post('/api/neutron/subnets/', newSubnet) + .error(function () { + horizon.alert('error', gettext('Unable to create the subnet.')); + }); + }; + + // Ports + + /** + * @name hz.api.neutronAPI.getPorts + * @description + * Get a list of ports for a network. + * + * The listing result is an object with property "items". Each item is + * a port. + * + * @param {string} network_id + * The network id to retrieve ports for. Required. + */ + this.getPorts = function(network_id) { + return apiService.get('/api/neutron/ports/', network_id) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve ports.')); + }); + }; + + } + + angular.module('hz.api') + .service('neutronAPI', ['apiService', NeutronAPI]); +}()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.nova.js horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.nova.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.nova.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.nova.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,327 @@ +/* +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. +*/ +/*global angular,horizon*/ +(function () { + 'use strict'; + + /** + * @ngdoc service + * @name hz.api.novaAPI + * @description Provides access to Nova APIs. + */ + function NovaAPI(apiService) { + + // Keypairs + + /** + * @name hz.api.novaAPI.getKeypairs + * @description + * Get a list of keypairs. + * + * The listing result is an object with property "items". Each item is + * a keypair. + */ + this.getKeypairs = function() { + return apiService.get('/api/nova/keypairs/') + .error(function () { + horizon.alert('error', gettext('Unable to retrieve keypairs.')); + }); + }; + + /** + * @name hz.api.novaAPI.createKeypair + * @description + * Create a new keypair. This returns the new keypair object on success. + * + * @param {Object} newKeypair + * The keypair to create. + * + * @param {string} newKeypair.name + * The name of the new keypair. Required. + * + * @param {string} newKeypair.public_key + * The public key. Optional. + */ + this.createKeypair = function(newKeypair) { + return apiService.post('/api/nova/keypairs/', newKeypair) + .error(function () { + horizon.alert('error', gettext('Unable to create the keypair.')); + }); + }; + + // Availability Zones + + /** + * @name hz.api.novaAPI.getAvailabilityZones + * @description + * Get a list of Availability Zones. + * + * The listing result is an object with property "items". Each item is + * an availability zone. + */ + this.getAvailabilityZones = function() { + return apiService.get('/api/nova/availzones/') + .error(function () { + horizon.alert('error', + gettext('Unable to retrieve availability zones.')); + }); + }; + + // Limits + + /** + * @name hz.api.novaAPI.getLimits + * @description + * Returns current limits. + * + * @example + * The following is an example response: + * { + * "maxImageMeta": 128, + * "maxPersonality": 5, + * "maxPersonalitySize": 10240, + * "maxSecurityGroupRules": 20, + * "maxSecurityGroups": 10, + * "maxServerGroupMembers": 10, + * "maxServerGroups": 10, + * "maxServerMeta": 128, + * "maxTotalCores": 20, + * "maxTotalFloatingIps": 10, + * "maxTotalInstances": 10, + * "maxTotalKeypairs": 100, + * "maxTotalRAMSize": 51200, + * "totalCoresUsed": 1, + * "totalFloatingIpsUsed": 0, + * "totalInstancesUsed": 1, + * "totalRAMUsed": 512, + * "totalSecurityGroupsUsed": 1, + * "totalServerGroupsUsed": 0 + * } + */ + this.getLimits = function() { + return apiService.get('/api/nova/limits/') + .error(function () { + horizon.alert('error', gettext('Unable to retrieve limits.')); + }); + }; + + // Servers + + /** + * @name hz.api.novaAPI.createServer + * @description + * Create a server using the parameters supplied in the + * newServer. The required parameters: + * + * "name", "source_id", "flavor_id", "key_name", "user_data" + * All strings + * "security_groups" + * An array of one or more objects with a "name" attribute. + * + * Other parameters are accepted as per the underlying novaclient: + * "block_device_mapping", "block_device_mapping_v2", "nics", "meta", + * "availability_zone", "instance_count", "admin_pass", "disk_config", + * "config_drive" + * + * This returns the new server object on success. + */ + this.createServer = function(newServer) { + return apiService.post('/api/nova/servers/', newServer) + .error(function () { + horizon.alert('error', gettext('Unable to create the server.')); + }); + }; + + /** + * @name hz.api.novaAPI.getServer + * @description + * Get a single server by ID + * @param {string} id + * Specifies the id of the server to request. + */ + this.getServer = function(id) { + return apiService.get('/api/nova/servers/' + id) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve server.')); + }); + }; + + /** + * @name hz.api.novaAPI.getExtensions + * @description + * Returns a list of enabled extensions. + * + * The listing result is an object with property "items". Each item is + * an extension. + * @example + * The following is an example response: + * + * { + * "items": [ + * { + * "alias": "NMN", + * "description": "Multiple network support.", + * "links": [], + * "name": "Multinic", + * "namespace": "http://docs.openstack.org/compute/ext/multinic/api/v1.1", + * "updated": "2011-06-09T00:00:00Z" + * } + * ] + * } + */ + this.getExtensions = function(config) { + return apiService.get('/api/nova/extensions/', config) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve extensions.')); + }); + }; + + /** + * @name hz.api.novaAPI.getFlavors + * @description + * Returns a list of flavors. + * + * The listing result is an object with property "items". Each item is + * a flavor. + * + * @param {boolean} isPublic (optional) + * True if public flavors should be returned. If not specified, the API + * will return public flavors by default for Admins and only project + * flavors for non-admins. + * @param {boolean} getExtras (optional) + * Also retrieve the extra specs. This is expensive (one extra underlying + * call per flavor). + */ + this.getFlavors = function(isPublic, getExtras) { + var config = {'params': {}}; + if (isPublic) { config.params.is_public = 'true'; } + if (getExtras) { config.params.get_extras = 'true'; } + return apiService.get('/api/nova/flavors/', config) + .success(function (data) { + // The colon character ':' in the flavor data causes problems when used + // in Angular $parse() statements. Since these values are used as keys + // to lookup data (and may end up in a $parse()) provide "user-friendly" + // attributes + if ( data && data.items ) { + data.items.map(function(item) { + if ( item.hasOwnProperty('OS-FLV-EXT-DATA:ephemeral')) { + item.ephemeral = item['OS-FLV-EXT-DATA:ephemeral']; + } + if ( item.hasOwnProperty('OS-FLV-DISABLED:disabled')) { + item.disabled = item['OS-FLV-DISABLED:disabled']; + } + if ( item.hasOwnProperty('os-flavor-access:is_public')) { + item.is_public = item['os-flavor-access:is_public']; + } + }); + } + }) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve flavors.')); + }); + }; + + /** + * @name hz.api.novaAPI.getFlavor + * @description + * Get a single flavor by ID. + * @param {string} id + * Specifies the id of the flavor to request. + * @param {boolean} getExtras (optional) + * Also retrieve the extra specs for the flavor. + */ + this.getFlavor = function(id, getExtras) { + var config = {'params': {}}; + if (getExtras) { config.params.get_extras = 'true'; } + return apiService.get('/api/nova/flavors/' + id, config) + .error(function () { + horizon.alert('error', gettext('Unable to retrieve flavor.')); + }); + }; + + /** + * @name hz.api.novaAPI.getFlavorExtraSpecs + * @description + * Get a single flavor's extra specs by ID. + * @param {string} id + * Specifies the id of the flavor to request the extra specs. + */ + this.getFlavorExtraSpecs = function(id) { + return apiService.get('/api/nova/flavors/' + id + '/extra-specs') + .error(function () { + horizon.alert('error', gettext('Unable to retrieve flavor extra specs.')); + }); + }; + } + + angular.module('hz.api') + .service('novaAPI', ['apiService', NovaAPI]); + + /** + * @ngdoc service + * @name hz.api.novaExtensions + * @description + * Provides cached access to Nova Extensions with utilities to help + * with asynchronous data loading. The cache may be reset at any time + * by accessing the cache and calling removeAll. The next call to any + * function will retrieve fresh results. + * + * The enabled extensions do not change often, so using cached data will + * speed up results. Even on a local devstack in informal testing, + * this saved between 30 - 100 ms per request. + */ + function NovaExtensions($cacheFactory, $q, novaAPI) { + + var service = {}; + service.cache = $cacheFactory('hz.api.novaExtensions', {capacity: 1}); + + service.get = function() { + return novaAPI.getExtensions({cache: service.cache}) + .then(function(data){ + return data.data.items; + } + ); + }; + + service.ifNameEnabled = function(desired, doThis) { + return service.get().then(function(extensions){ + if (enabled(extensions, 'name', desired)){ + return $q.when(doThis()); + } + } + ); + }; + + function enabled(resources, key, desired) { + if(resources) { + return resources.some(function (resource) { + return resource[key] === desired; + }); + } else { + return false; + } + } + + return service; + } + + angular.module('hz.api') + .factory('novaExtensions', ['$cacheFactory', + '$q', + 'novaAPI', + NovaExtensions]); + +}()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.policy.js horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.policy.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.policy.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.policy.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,72 @@ +/* +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 hz.api.policyAPI + * @description Provides a direct pass through to the policy engine in + * Horizon. + */ + function PolicyService(apiService) { + + /** + * @name hz.api.policyAPI.check + * @description + * Check the passed in policy rule list to determine if the user has + * permission to perform the actions specified by the rules. The service + * APIs will ultimately reject actions that are not permitted. This is used + * for Role Based Access Control in the UI only. The required parameter + * should have the following structure: + * + * { + * "rules": [ + * [ "compute", "compute:get_all" ], + * ], + * "target": { + * "project_id": "1" + * } + * } + * + * where "rules" is a list of rules (1 or greater in length) policy rules + * which are composed of a + * * service name -- maps the policy rule to a service + * * rule -- the policy rule to check + * and "target" key and value is optional. In some cases, policy rules + * require specific details about the object that is to be acted on. + * If added, it is merely a dictionary of keys and values. + * + * + * The following is the response if the check passes: + * { + * "allowed": true + * } + * + * The following is the response if the check fails: + * { + * "allowed": false + * } + */ + this.check = function (policy_rules) { + return apiService.post('/api/policy/', policy_rules) + .error(function() { + horizon.alert('warning', gettext('Policy check failed.')); + }); + }; + } + + angular.module('hz.api') + .service('policyAPI', ['apiService', PolicyService]); +}()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.security-group.js horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.security-group.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.security-group.js 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.security-group.js 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,75 @@ +/* +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 service + * @name hz.api.SecurityGroup + * @description Provides access to Security Groups + */ + function SecurityGroup(apiService) { + + /** + * @name hz.api.SecurityGroup.list + * @description + * Get a list of security groups. + * + * The listing result is an object with property "items". Each item is + * an image. + * + * @example + * The following is an example response: + * { + * "items": [ + * { + * "description": "Default security group", + * "id": "4a4c9dd4-ffa0-454a-beaa-23e8fa569062", + * "name": "default", + * "security_group_rules": [ + * { + * "direction": "ingress", + * "ethertype": "IPv4", + * "id": "22961412-fba1-4d0d-8eb8-d4460c921346", + * "port_range_max": null, + * "port_range_min": null, + * "protocol": null, + * "remote_group_id": "4a4c9dd4-ffa0-454a-beaa-23e8fa569062", + * "remote_ip_prefix": null, + * "security_group_id": "4a4c9dd4-ffa0-454a-beaa-23e8fa569062", + * "tenant_id": "3f867827f7eb45d4aa1d1395237f426b" + * } + * ], + * "tenant_id": "3f867827f7eb45d4aa1d1395237f426b" + * } + * ] + * } + */ + this.query = function() { + return apiService.get('/api/network/securitygroups/') + .error(function () { + horizon.alert('error', gettext('Unable to retrieve security groups.')); + }); + }; + } + + // Register it with the API module so that anybody using the + // API module will have access to the Security Group APIs. + + angular.module('hz.api') + .service('securityGroup', ['apiService', SecurityGroup]); + +}()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.service.js horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.service.js --- horizon-2015.1~b2/horizon/static/horizon/js/angular/services/hz.api.service.js 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/angular/services/hz.api.service.js 2015-03-19 19:07:23.000000000 +0000 @@ -62,9 +62,13 @@ if (angular.isDefined(data)) { config.data = data; } - var r = new ApiPromise(); - $http(config).success(r.handleSuccess).error(r.handleError); - return r; + + // + // TODO: need discussion with Richard for this change. + // The reason for this change is to get a promise object compatible + // to $q.defer().promise. + // + return $http(config); }; this.get = function(url, config) { @@ -87,4 +91,4 @@ angular.module('hz.api.service', []) .service('apiService', ['$http', '$log', ApiService]); -}()); \ No newline at end of file +}()); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/horizon.accordion_nav.js horizon-2015.1~b3/horizon/static/horizon/js/horizon.accordion_nav.js --- horizon-2015.1~b2/horizon/static/horizon/js/horizon.accordion_nav.js 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/horizon.accordion_nav.js 2015-03-19 19:07:23.000000000 +0000 @@ -1,13 +1,6 @@ horizon.addInitFunction(function() { var allPanelGroupBodies = $('.nav_accordion > dd > div > ul'); - allPanelGroupBodies.each(function(index, value) { - var activePanels = $(this).find('li > a.active'); - if(activePanels.length === 0) { - $(this).slideUp(0); - } - }); - // mark the active panel group var activePanel = $('.nav_accordion > dd > div > ul > li > a.active'); activePanel.closest('div').find('h4').addClass('active'); @@ -38,7 +31,7 @@ if (activeDashPanel.length === 0) { // expand the active panel group var activePanel = myDashBody.find("div:first > ul"); - activePanel.slideDown(); + activePanel.removeClass('hidden').slideDown(); activePanel.closest('div').find("h4").addClass("active"); // collapse the inactive panel groups @@ -50,7 +43,7 @@ { // collapse the inactive panel groups activeDashPanel.closest('div').find("h4").addClass("active"); - activeDashPanel.closest('ul').slideDown(); + activeDashPanel.closest('ul').removeClass('hidden').slideDown(); allPanelGroupBodies.each(function(index, value) { var activePanels = $(value).find('li > a.active'); if(activePanels.length === 0) { @@ -75,7 +68,8 @@ // expand the selected panel group if not already active if(!myPanelGroupWasActive) { myPanelGroupHeader.addClass("active"); - myPanelGroupHeader.closest('div').find('ul').slideDown(); + myPanelGroupHeader.closest('div').find('ul').removeClass('hidden') + .slideDown(); } }); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/horizon.forms.js horizon-2015.1~b3/horizon/static/horizon/js/horizon.forms.js --- horizon-2015.1~b2/horizon/static/horizon/js/horizon.forms.js 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/horizon.forms.js 2015-03-19 19:07:23.000000000 +0000 @@ -77,7 +77,7 @@ $filename = $filename.substring(1); } - if (typeof($obj_name.val()) === 'undefined' || $(this).attr("filename").localeCompare($obj_name.val()) === 0) { + if (typeof($obj_name.val()) === 'undefined' || $obj_name.val().length < 1 || $(this).attr("filename").localeCompare($obj_name.val()) === 0) { $obj_name.val($filename); $(this).attr("filename", $filename); $obj_name.trigger('input'); @@ -204,15 +204,19 @@ } // Bind event handlers to confirm dangerous actions. - $("body").on("click", "form button.btn-danger", function (evt) { + // Stops angular form buttons from triggering this event + $("body").on("click", "form button:not([ng-click]).btn-danger", function (evt) { horizon.datatables.confirm(this); evt.preventDefault(); }); /* Switchable Fields (See Horizon's Forms docs for more information) */ + // Single reference + var $document = $(document); + // Bind handler for swapping labels on "switchable" select fields. - $(document).on("change", 'select.switchable', function (evt) { + $document.on("change", 'select.switchable', function (evt) { var $fieldset = $(evt.target).closest('fieldset'), $switchables = $fieldset.find('select.switchable'); @@ -247,7 +251,7 @@ }); // Bind handler for swapping labels on "switchable" checkbox input fields. - $(document).on("change", 'input.switchable', function (evt) { + $document.on("change", 'input.switchable', function (evt) { var $fieldset = $(evt.target).closest('fieldset'), $switchables = $fieldset.find('input.switchable'); @@ -320,7 +324,7 @@ }); } - $(document).on('change', '#id_volume_source_type', function (evt) { + $document.on('change', '#id_volume_source_type', function (evt) { update_volume_source_displayed_fields(this); }); @@ -332,7 +336,7 @@ /* Help tooltips */ // Apply standard handler for everything but checkboxes. - $(document).tooltip({ + $document.tooltip({ selector: "div.form-group .help-icon", placement: function (tip, input) { // Position to the right unless this is a "split" for in which case put @@ -345,11 +349,11 @@ }); // Hide the tooltip upon interaction with the field for select boxes. // We use mousedown and keydown since those "open" the select dropdown. - $(document).on('mousedown keydown', '.form-group select', function (evt) { + $document.on('mousedown keydown', '.form-group select', function (evt) { $(this).tooltip('hide'); }); // Hide the tooltip after escape button pressed - $(document).on('keydown.esc_btn', function (evt) { + $document.on('keydown.esc_btn', function (evt) { if (evt.keyCode === 27) { $('.tooltip').hide(); } diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/horizon.instances.js horizon-2015.1~b3/horizon/static/horizon/js/horizon.instances.js --- horizon-2015.1~b2/horizon/static/horizon/js/horizon.instances.js 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/horizon.instances.js 2015-03-19 19:07:23.000000000 +0000 @@ -167,34 +167,35 @@ // Handle field toggles for the Launch Instance source type field function update_launch_source_displayed_fields (field) { - var $this = $(field), - base_type = $this.val(); + var $this = $(field); + var base_type = $this.val(); + var elements_list; $this.closest(".form-group").nextAll().hide(); switch(base_type) { case "image_id": - $("#id_image_id").closest(".form-group").show(); + elements_list = "#id_image_id"; break; - case "instance_snapshot_id": - $("#id_instance_snapshot_id").closest(".form-group").show(); + elements_list = "#id_instance_snapshot_id"; break; - case "volume_id": - $("#id_volume_id, #id_device_name, #id_delete_on_terminate").closest(".form-group").show(); + elements_list = "#id_volume_id, #id_device_name, #id_delete_on_terminate"; break; - case "volume_image_id": - $("#id_image_id, #id_volume_size, #id_device_name, #id_delete_on_terminate") - .closest(".form-group").show(); + elements_list = "#id_image_id, #id_volume_size, #id_device_name, #id_delete_on_terminate"; break; - case "volume_snapshot_id": - $("#id_volume_snapshot_id, #id_device_name, #id_delete_on_terminate") - .closest(".form-group").show(); + elements_list = "#id_volume_snapshot_id, #id_device_name, #id_delete_on_terminate"; break; } + var elements_list_group = $(elements_list).closest(".form-group"); + elements_list_group.addClass("required"); + // marking all the fields in 'elements_list' as mandatory except '#id_device_name' + $("#id_device_name").closest(".form-group").removeClass("required"); + // showing all the fields in 'elements_list' + elements_list_group.show(); } $document.on('change', '.workflow #id_source_type', function (evt) { diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/horizon.modals.js horizon-2015.1~b3/horizon/static/horizon/js/horizon.modals.js --- horizon-2015.1~b2/horizon/static/horizon/js/horizon.modals.js 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/horizon.modals.js 2015-03-19 19:07:23.000000000 +0000 @@ -170,19 +170,21 @@ horizon.addInitFunction(horizon.modals.init = function() { + var $document = $(document); + // Bind handler for initializing new modals. $('#modal_wrapper').on('new_modal', function (evt, modal) { horizon.modals.initModal(modal); }); // Bind "cancel" button handler. - $(document).on('click', '.modal .cancel', function (evt) { + $document.on('click', '.modal .cancel', function (evt) { $(this).closest('.modal').modal('hide'); evt.preventDefault(); }); // AJAX form submissions from modals. Makes validation happen in-modal. - $(document).on('submit', '.modal form', function (evt) { + $document.on('submit', '.modal form', function (evt) { var $form = $(this), form = this, $button = $form.find(".modal-footer .btn-primary"), @@ -271,7 +273,7 @@ }); // Position modal so it's in-view even when scrolled down. - $(document).on('show.bs.modal', '.modal', function (evt) { + $document.on('show.bs.modal', '.modal', function (evt) { // Filter out indirect triggers of "show" from (for example) tabs. if ($(evt.target).hasClass("modal")) { var scrollShift = $('body').scrollTop() || $('html').scrollTop(), @@ -300,7 +302,7 @@ horizon.modals.addModalInitFunction(horizon.utils.loadAngular); // Load modals for ajax-modal links. - $(document).on('click', '.ajax-modal', function (evt) { + $document.on('click', '.ajax-modal', function (evt) { var $this = $(this); // If there's an existing modal request open, cancel it out. @@ -354,7 +356,7 @@ // After a modal has been shown, hide any other modals that are already in // the stack. Only one modal can be visible at the same time. - $(document).on("show.bs.modal", ".modal", function () { + $document.on("show.bs.modal", ".modal", function () { var modal_stack = $("#modal_wrapper .modal"); modal_stack.splice(modal_stack.length - 1, 1); modal_stack.modal("hide"); @@ -364,7 +366,7 @@ // Note: the modal should only be removed if it is the "top" of the stack of // modals, e.g. it's the one currently being interacted with and isn't just // temporarily being hidden. - $(document).on('hidden.bs.modal', '.modal', function () { + $document.on('hidden.bs.modal', '.modal', function () { var $this = $(this), modal_stack = $("#modal_wrapper .modal"); if ($this[0] === modal_stack.last()[0] || $this.hasClass("loading")) { @@ -376,7 +378,7 @@ }); // Make modals draggable - $(document).on("show.bs.modal", ".modal", function () { + $document.on("show.bs.modal", ".modal", function () { $(".modal-content").draggable({ handle: ".modal-header" }); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/horizon.tables_inline_edit.js horizon-2015.1~b3/horizon/static/horizon/js/horizon.tables_inline_edit.js --- horizon-2015.1~b2/horizon/static/horizon/js/horizon.tables_inline_edit.js 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/horizon.tables_inline_edit.js 2015-03-19 19:07:23.000000000 +0000 @@ -100,7 +100,6 @@ self.form_element = self.get_form_element(td_element); if (self.inline_edit_mod) { - var cellWidth = self.td_element.outerWidth(true); td_element.width(cellWidth); td_element.addClass("has-form"); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/horizon.tables.js horizon-2015.1~b3/horizon/static/horizon/js/horizon.tables.js --- horizon-2015.1~b2/horizon/static/horizon/js/horizon.tables.js 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/horizon.tables.js 2015-03-19 19:07:23.000000000 +0000 @@ -307,6 +307,10 @@ // innertext smashed together with no delimiters, so we need to find // the original cell and grab its first element to do the work var a = $(cell).find('li').first().text().split('.'); + // if the cell doesn't contains the list, try to get the text directly + if (a === "") { + a = $(cell).text().split('.'); + } if (a.length !== 4) { return false; } @@ -323,6 +327,10 @@ format: function(s, table, cell) { var result = 0; var a = $(cell).find('li').first().text().split('.'); + // if the cell doesn't contains the list, try to get the text directly + if (a === "") { + a = $(cell).text().split('.'); + } var last_index = a.length - 1; // inet_aton(3), Javascript-style. The unsigned-right-shift operation is // needed to keep the result from flipping over to negative when suitably diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/horizon.tabs.js horizon-2015.1~b3/horizon/static/horizon/js/horizon.tabs.js --- horizon-2015.1~b2/horizon/static/horizon/js/horizon.tabs.js 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/horizon.tabs.js 2015-03-19 19:07:23.000000000 +0000 @@ -44,9 +44,12 @@ $(el).find(".js-tab-pane").addClass("tab-pane"); }); - $(document).on("show.bs.tab", ".ajax-tabs a[data-loaded='false']", horizon.tabs.load_tab); - $(document).on("shown.bs.tab", ".nav-tabs a[data-toggle='tab']", function (evt) { + var $document = $(document); + + $document.on("show.bs.tab", ".ajax-tabs a[data-loaded='false']", horizon.tabs.load_tab); + + $document.on("shown.bs.tab", ".nav-tabs a[data-toggle='tab']", function (evt) { var $tab = $(evt.target), $content = $($(evt.target).attr('data-target')); $content.find("table.datatable").each(function () { @@ -68,7 +71,7 @@ }); // Enable keyboard navigation between tabs in a form. - $(document).on("keydown", ".tab-pane :input:visible:last", function (evt) { + $(".tab-content").on("keydown", ".tab-pane :input:visible:last", function (evt) { var $this = $(this), next_pane = $this.closest(".tab-pane").next(".tab-pane"); // Capture the forward-tab keypress if we have a next tab to go to. @@ -77,7 +80,7 @@ $(".nav-tabs a[data-target='#" + next_pane.attr("id") + "']").tab('show'); } }); - $(document).on("keydown", ".tab-pane :input:visible:first", function (evt) { + $(".tab-content").on("keydown", ".tab-pane :input:visible:first", function (evt) { var $this = $(this), prev_pane = $this.closest(".tab-pane").prev(".tab-pane"); // Capture the forward-tab keypress if we have a next tab to go to. @@ -88,7 +91,7 @@ } }); - $(document).on("focus", ".tab-content :input", function () { + $document.on("focus", ".tab-content :input", function () { var $this = $(this), tab_pane = $this.closest(".tab-pane"), tab_id = tab_pane.attr('id'); diff -Nru horizon-2015.1~b2/horizon/static/horizon/js/horizon.users.js horizon-2015.1~b3/horizon/static/horizon/js/horizon.users.js --- horizon-2015.1~b2/horizon/static/horizon/js/horizon.users.js 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/js/horizon.users.js 2015-03-19 19:07:23.000000000 +0000 @@ -13,19 +13,20 @@ }, check_passwords_match: function() { - var row = $("label[for='id_confirm_password']"); + var row = $("input#id_confirm_password"); var error_id = "id_confirm_password_error"; - var msg = "" + gettext("Passwords do not match.") + ""; + var msg = "" + gettext("Passwords do not match.") + ""; var password = $("#id_password").val(); var confirm_password = $("#id_confirm_password").val(); if (password !== confirm_password && $("#" + error_id).length === 0) { - $(row).parent().addClass("error"); + $(row).parent().addClass("has-error"); $(row).after(msg); } else if (password === confirm_password) { - $(row).parent().removeClass("error"); + $(row).parent().removeClass("has-error"); $("#" + error_id).remove(); } } }; + diff -Nru horizon-2015.1~b2/horizon/static/horizon/tests/jasmine/metadataWidgetControllerSpec.js horizon-2015.1~b3/horizon/static/horizon/tests/jasmine/metadataWidgetControllerSpec.js --- horizon-2015.1~b2/horizon/static/horizon/tests/jasmine/metadataWidgetControllerSpec.js 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/static/horizon/tests/jasmine/metadataWidgetControllerSpec.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,96 +0,0 @@ -/*global describe, it, expect, jasmine, beforeEach, spyOn, angular*/ -describe('metadata-widget-controller', function () { - 'use strict'; - var $scope; - beforeEach(function () { - angular.mock.module('hz'); - }); - - beforeEach(function () { - angular.mock.inject(function ($injector) { - var gettext = function (text) { - return text; - }; - var $window = { - available_metadata: {namespaces: []}, - gettext: gettext - }; - $scope = $injector.get('$rootScope').$new(); - var metadataController = $injector.get('$controller')( - 'hzMetadataWidgetCtrl', - { - $scope: $scope, - $window: $window - }); - }); - }); - - describe('formatErrorMessage', function () { - it('should return undefined', function () { - expect($scope.formatErrorMessage('test', 'test')).toBe(undefined); - }); - - it('should return "Min 2"', function () { - var error, item; - error = {min: true}; - item = {leaf: {minimum: '2'}}; - expect($scope.formatErrorMessage(item, error)).toBe('Min 2'); - }); - - it('should return "Max 2"', function () { - var error, item; - error = {max: true}; - item = {leaf: {maximum: '2'}}; - expect($scope.formatErrorMessage(item, error)).toBe('Max 2'); - }); - - it('should return "Min length 5"', function () { - var error, item; - error = {minlength: true}; - item = {leaf: {minLength: '5'}}; - expect($scope.formatErrorMessage(item, error)).toBe('Min length 5'); - }); - - it('should return "Max length 5"', function () { - var error, item; - error = {maxlength: true}; - item = {leaf: {maxLength: '5'}}; - expect($scope.formatErrorMessage(item, error)).toBe('Max length 5'); - }); - - it('should return "Integer required"', function () { - var error, item; - error = {pattern: true}; - item = {leaf: {type: 'integer'}}; - expect($scope.formatErrorMessage(item, error)).toBe('Integer required'); - }); - - it('should return "Pattern mismatch"', function () { - var error, item; - error = {pattern: true}; - item = {leaf: {type: 'wrong pattern'}}; - expect($scope.formatErrorMessage(item, error)).toBe('Pattern mismatch'); - }); - - it('should return "Integer required"', function () { - var error, item; - error = {required: true}; - item = {leaf: {type: 'integer'}}; - expect($scope.formatErrorMessage(item, error)).toBe('Integer required'); - }); - - it('should return "Decimal required"', function () { - var error, item; - error = {required: true}; - item = {leaf: {type: 'number'}}; - expect($scope.formatErrorMessage(item, error)).toBe('Decimal required'); - }); - - it('should return "Integer required"', function () { - var error, item; - error = {required: true}; - item = {leaf: {type: 'mock'}}; - expect($scope.formatErrorMessage(item, error)).toBe('Required'); - }); - }); -}); diff -Nru horizon-2015.1~b2/horizon/tables/actions.py horizon-2015.1~b3/horizon/tables/actions.py --- horizon-2015.1~b2/horizon/tables/actions.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/tables/actions.py 2015-03-19 19:07:29.000000000 +0000 @@ -52,7 +52,7 @@ initialized clean way. Similar principle is used in DataTableMetaclass. """ def __new__(mcs, name, bases, attrs): - # Options of action are set ass class attributes, loading them. + # Options of action are set as class attributes, loading them. options = {} if attrs: options = attrs @@ -800,7 +800,7 @@ action_not_allowed = [] for datum_id in obj_ids: datum = table.get_object_by_id(datum_id) - datum_display = table.get_object_display(datum) or _("N/A") + datum_display = table.get_object_display(datum) or datum_id if not table._filter_action(self, request, datum): action_not_allowed.append(datum_display) LOG.info('Permission denied to %s: "%s"' % diff -Nru horizon-2015.1~b2/horizon/tables/views.py horizon-2015.1~b3/horizon/tables/views.py --- horizon-2015.1~b2/horizon/tables/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/tables/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -15,7 +15,8 @@ from collections import defaultdict from django import shortcuts -from django.views import generic + +from horizon import views from horizon.templatetags.horizon import has_permissions # noqa @@ -125,7 +126,7 @@ return handled -class MultiTableView(MultiTableMixin, generic.TemplateView): +class MultiTableView(MultiTableMixin, views.HorizonTemplateView): """A class-based generic view to handle the display and processing of multiple :class:`~horizon.tables.DataTable` classes in a single view. @@ -136,6 +137,7 @@ which returns a set of data for that table; and specify a template for the ``template_name`` attribute. """ + def construct_tables(self): tables = self.get_tables().values() # Early out before data is loaded diff -Nru horizon-2015.1~b2/horizon/tabs/base.py horizon-2015.1~b3/horizon/tabs/base.py --- horizon-2015.1~b2/horizon/tabs/base.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/tabs/base.py 2015-03-19 19:07:23.000000000 +0000 @@ -48,6 +48,11 @@ across requests for a given user. (State storage is all done client-side.) + .. attribute:: show_single_tab + + Boolean to control whether the tab bar is shown when the tab group + has only one tab. Default: ``False`` + .. attribute:: param_name The name of the GET request parameter which will be used when @@ -77,6 +82,7 @@ template_name = "horizon/common/_tab_group.html" param_name = 'tab' sticky = False + show_single_tab = False _selected = None _active = None diff -Nru horizon-2015.1~b2/horizon/tabs/views.py horizon-2015.1~b3/horizon/tabs/views.py --- horizon-2015.1~b2/horizon/tabs/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/tabs/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -11,14 +11,14 @@ # under the License. from django import http -from django.views import generic from horizon import exceptions from horizon import tables from horizon.tabs.base import TableTab # noqa +from horizon import views -class TabView(generic.TemplateView): +class TabView(views.HorizonTemplateView): """A generic class-based view for displaying a :class:`horizon.tabs.TabGroup`. diff -Nru horizon-2015.1~b2/horizon/templates/base.html horizon-2015.1~b3/horizon/templates/base.html --- horizon-2015.1~b2/horizon/templates/base.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/templates/base.html 2015-03-19 19:07:23.000000000 +0000 @@ -33,7 +33,9 @@
- {% block page_header %}{% endblock %} + {% block page_header %} + {% include "horizon/common/_page_header.html" with title=page_title %} + {% endblock %} {% block main %}{% endblock %}
diff -Nru horizon-2015.1~b2/horizon/templates/horizon/_accordion_nav.html horizon-2015.1~b3/horizon/templates/horizon/_accordion_nav.html --- horizon-2015.1~b2/horizon/templates/horizon/_accordion_nav.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/templates/horizon/_accordion_nav.html 2015-03-19 19:07:23.000000000 +0000 @@ -4,39 +4,37 @@ diff -Nru horizon-2015.1~b2/horizon/templates/horizon/common/_data_table_cell.html horizon-2015.1~b3/horizon/templates/horizon/common/_data_table_cell.html --- horizon-2015.1~b2/horizon/templates/horizon/common/_data_table_cell.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/templates/horizon/common/_data_table_cell.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,7 +1,7 @@ {% if cell.inline_edit_mod and cell.update_allowed %}
-
+
{{ cell.value }} {% if cell.column.form_field.label %} @@ -11,10 +11,10 @@
@@ -34,6 +34,8 @@
{% else %} - {% if cell.wrap_list %}
    {% endif %}{{ cell.value }}{% if cell.wrap_list %}
{% endif %} + + {% if cell.wrap_list %}
    {% endif %}{{ cell.value }}{% if cell.wrap_list %}
{% endif %} + {% endif %} {% endif %} diff -Nru horizon-2015.1~b2/horizon/templates/horizon/common/_modal_form_update_metadata.html horizon-2015.1~b3/horizon/templates/horizon/common/_modal_form_update_metadata.html --- horizon-2015.1~b2/horizon/templates/horizon/common/_modal_form_update_metadata.html 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/templates/horizon/common/_modal_form_update_metadata.html 2015-03-19 19:07:29.000000000 +0000 @@ -4,225 +4,20 @@ {% block form_name %}metadataForm{% endblock %} {% block form_validation %}novalidate{% endblock %} -{% block ng_controller %}hzMetadataWidgetCtrl{% endblock %} +{% block ng_controller %}hzModalFormUpdateMetadataCtrl{% endblock %} {% block modal-body %} -
-
-

{% blocktrans %} - You can specify metadata by adding items from the left column to - the right column. You may select the metadata added to glance - dictionary or you can use the "Other" option using a key of - your choice. - {% endblocktrans %} -

-
-
-
-
-
-
-
- {% trans "Available Metadata" %} - - -
-
-
-
    -
  • -
    - {% trans "Other" %} - - - - - - -
    -
  • -
  • - - {% trans "Duplicate keys are not allowed" %} - -
  • -
  • -
    -
    - - - {$ item.label $} - -
    -
    - - - -
    -
    -
  • -
  • - {% trans "No available metadata" %} -
  • -
-
-
- -
-
-
-
-
- {% trans "Existing Metadata" %} - - -
-
-
-
    -
  • -
    - - {$ item.leaf.name $} - - - - - - -
    - -
    -
    - - - -
    -
    -
    - {$ item.path().join(' › ') $} -
    -
    - {$ formatErrorMessage(item, itemForm.property.$error) $} -
    -
  • -
  • - {% trans "No available metadata" %} -
  • -
-
-
-
- -
- -

- {$ selected.label $} - ({$ selected.leaf.name $}) -

-

{$ selected.description $}

-
- -

{% blocktrans %} - You can specify resource metadata by moving items from the left - column to the right column. In the left columns there are metadata - definitions from the Glance Metadata Catalog. Use the "Other" option - to add metadata with the key of your choice. - {% endblocktrans %} -

-
-
-
+ + {% endblock %} - {% block modal-footer %}
1 %}
    {% for tab in tabs %}
  • @@ -9,6 +10,7 @@
  • {% endfor %}
+ {% endif %} {# Tab Content #}
diff -Nru horizon-2015.1~b2/horizon/templates/horizon/_conf.html horizon-2015.1~b3/horizon/templates/horizon/_conf.html --- horizon-2015.1~b2/horizon/templates/horizon/_conf.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/templates/horizon/_conf.html 2015-03-19 19:07:23.000000000 +0000 @@ -8,7 +8,6 @@ - {% endcompress %} diff -Nru horizon-2015.1~b2/horizon/templates/horizon/jasmine/jasmine.html horizon-2015.1~b3/horizon/templates/horizon/jasmine/jasmine.html --- horizon-2015.1~b2/horizon/templates/horizon/jasmine/jasmine.html 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/templates/horizon/jasmine/jasmine.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,28 +1,46 @@ +{% load url from future %} Jasmine Spec Runner - - - + + + + + + + + + + + + - - - - - - {% for source in sources %} - + + {% for file in sources %} + + {% endfor %} + + + {% for file in specs %} + + {% endfor %} + + + {% for file in HORIZON_CONFIG.js_files %} + {% endfor %} - {% for spec in specs %} - + + {% for file in HORIZON_CONFIG.js_spec_files %} + {% endfor %} diff -Nru horizon-2015.1~b2/horizon/templates/horizon/_scripts.html horizon-2015.1~b3/horizon/templates/horizon/_scripts.html --- horizon-2015.1~b2/horizon/templates/horizon/_scripts.html 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/templates/horizon/_scripts.html 2015-03-19 19:07:29.000000000 +0000 @@ -10,64 +10,95 @@ {% comment %} Compress jQuery, Angular, Plugins, Bootstrap, Hogan.js and Horizon-specific JS. {% endcomment %} {% compress js %} -{% for file in HORIZON_CONFIG.js_files %} - -{% endfor %} - + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + +{% for file in HORIZON_CONFIG.js_files %} + +{% endfor %} - {% block custom_js_files %}{% endblock %} {% endcompress %} @@ -76,11 +107,11 @@ {% comment %}Locale sensitive js needs to be included outisde the "compress" tag{% endcomment %} {% if DATEPICKER_LOCALE and DATEPICKER_LOCALE != 'en' %} - + {% endif %} {% comment %}Go!{% endcomment %} - diff -Nru horizon-2015.1~b2/horizon/templatetags/horizon.py horizon-2015.1~b3/horizon/templatetags/horizon.py --- horizon-2015.1~b2/horizon/templatetags/horizon.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/templatetags/horizon.py 2015-03-19 19:07:23.000000000 +0000 @@ -49,6 +49,7 @@ 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(): @@ -63,6 +64,8 @@ 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.name if allowed_panels: non_empty_groups.append((group.name, allowed_panels)) if (callable(dash.nav) and dash.nav(context) and @@ -74,6 +77,7 @@ 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']} diff -Nru horizon-2015.1~b2/horizon/templatetags/sizeformat.py horizon-2015.1~b3/horizon/templatetags/sizeformat.py --- horizon-2015.1~b2/horizon/templatetags/sizeformat.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/templatetags/sizeformat.py 2015-03-19 19:07:23.000000000 +0000 @@ -20,6 +20,8 @@ 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 _ @@ -49,24 +51,19 @@ return ungettext_lazy("%(size)d Byte", "%(size)d Bytes", 0) % {'size': 0} - if bytes < 1024: + if bytes < units.Ki: bytes = int(bytes) return ungettext_lazy("%(size)d Byte", "%(size)d Bytes", bytes) % {'size': bytes} - if bytes < 1024 * 1024: - return _("%s KB") % \ - filesize_number_format(bytes / 1024) - if bytes < 1024 * 1024 * 1024: - return _("%s MB") % \ - filesize_number_format(bytes / (1024 * 1024)) - if bytes < 1024 * 1024 * 1024 * 1024: - return _("%s GB") % \ - filesize_number_format(bytes / (1024 * 1024 * 1024)) - if bytes < 1024 * 1024 * 1024 * 1024 * 1024: - return _("%s TB") % \ - filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024)) - return _("%s PB") % \ - filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024 * 1024)) + 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): @@ -80,14 +77,14 @@ @register.filter(name='mbformat') def mbformat(mb): - return float_cast_filesizeformat(mb, 1024 * 1024, int_format) + 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, 1024 * 1024, float_format) + return float_cast_filesizeformat(mb, units.Mi, float_format) @register.filter(name='diskgbformat') def diskgbformat(gb): - return float_cast_filesizeformat(gb, 1024 * 1024 * 1024, float_format) + return float_cast_filesizeformat(gb, units.Gi, float_format) diff -Nru horizon-2015.1~b2/horizon/test/helpers.py horizon-2015.1~b3/horizon/test/helpers.py --- horizon-2015.1~b2/horizon/test/helpers.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/test/helpers.py 2015-03-19 19:07:23.000000000 +0000 @@ -284,14 +284,14 @@ wait.until(jasmine_done) failures = \ - self.selenium.find_elements_by_css_selector(".specDetail.failed") + self.selenium.find_elements_by_css_selector(".spec-detail.failed") results = [] for failure in failures: results.append( failure.find_element_by_class_name("description").text) results.append( - failure.find_element_by_class_name("stackTrace").text) + failure.find_element_by_class_name("stack-trace").text) self.assertEqual(results, [], '\n\n' + '\n\n'.join(results) + '\n\n') diff -Nru horizon-2015.1~b2/horizon/test/jasmine/jasmine_tests.py horizon-2015.1~b3/horizon/test/jasmine/jasmine_tests.py --- horizon-2015.1~b2/horizon/test/jasmine/jasmine_tests.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/test/jasmine/jasmine_tests.py 2015-03-19 19:07:29.000000000 +0000 @@ -10,30 +10,68 @@ # License for the specific language governing permissions and limitations # under the License. -from django.utils import unittest from horizon.test import helpers as test -@unittest.skip("Temporarily disabled, see bug #1396954") class ServicesTests(test.JasmineTests): sources = [ 'horizon/js/horizon.js', 'horizon/js/angular/horizon.conf.js', 'horizon/js/angular/horizon.js', 'horizon/js/angular/services/horizon.utils.js', - 'horizon/js/angular/controllers/metadata-widget-controller.js', 'horizon/js/angular/hz.api.module.js', 'horizon/js/angular/services/hz.api.service.js', 'angular/widget.module.js', + 'angular/action-list/action-list.js', + 'angular/action-list/button-tooltip.js', + 'angular/bind-scope/bind-scope.js', + 'angular/charts/charts.js', + 'angular/charts/chart-tooltip.js', + 'angular/charts/pie-chart.js', + 'angular/form/form.js', 'angular/help-panel/help-panel.js', + 'angular/metadata-tree/metadata-tree.js', + 'angular/metadata-tree/metadata-tree-service.js', + 'angular/modal/modal.js', + 'angular/modal-wait-spinner/modal-wait-spinner.js', + 'angular/table/table.js', + 'angular/table/basic-table.js', + 'angular/transfer-table/transfer-table.js', + 'angular/wizard/wizard.js', + 'angular/metadata-display/metadata-display.js', + 'horizon/js/angular/filters/filters.js', ] specs = [ - 'horizon/tests/jasmine/utilsSpec.js', - 'horizon/tests/jasmine/metadataWidgetControllerSpec.js', 'horizon/js/angular/services/hz.api.service.spec.js', + 'horizon/tests/jasmine/utilsSpec.js', + 'angular/action-list/action-list.spec.js', + 'angular/bind-scope/bind-scope.spec.js', + 'angular/charts/pie-chart.spec.js', + 'angular/form/form.spec.js', 'angular/help-panel/help-panel.spec.js', + 'angular/modal/simple-modal.spec.js', + 'angular/table/table.spec.js', + 'angular/table/basic-table.spec.js', + 'angular/transfer-table/transfer-table.spec.js', + 'angular/wizard/wizard.spec.js', + 'angular/metadata-tree/metadata-tree.spec.js', + 'angular/metadata-display/metadata-display.spec.js', + 'horizon/js/angular/filters/filters.spec.js', ] externalTemplates = [ + 'angular/action-list/action.html', + 'angular/action-list/menu-item.html', + 'angular/action-list/menu.html', + 'angular/action-list/single-button.html', + 'angular/action-list/split-button.html', + 'angular/charts/chart-tooltip.html', + 'angular/charts/pie-chart.html', 'angular/help-panel/help-panel.html', + 'angular/table/search-bar.html', + 'angular/transfer-table/transfer-table.html', + 'angular/wizard/wizard.html', + 'angular/metadata-tree/metadata-tree.html', + 'angular/metadata-tree/metadata-tree-item.html', + 'angular/metadata-display/metadata-display.html', ] diff -Nru horizon-2015.1~b2/horizon/test/settings.py horizon-2015.1~b3/horizon/test/settings.py --- horizon-2015.1~b2/horizon/test/settings.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/test/settings.py 2015-03-19 19:07:29.000000000 +0000 @@ -73,9 +73,17 @@ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.doc.XViewMiddleware', +) +if django.VERSION >= (1, 8, 0): + MIDDLEWARE_CLASSES += ( + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',) +else: + MIDDLEWARE_CLASSES += ('django.middleware.doc.XViewMiddleware',) +MIDDLEWARE_CLASSES += ( + 'horizon.middleware.HorizonMiddleware', 'django.middleware.locale.LocaleMiddleware', - 'horizon.middleware.HorizonMiddleware') + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +) TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.debug', diff -Nru horizon-2015.1~b2/horizon/test/tests/tables.py horizon-2015.1~b3/horizon/test/tests/tables.py --- horizon-2015.1~b2/horizon/test/tests/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/horizon/test/tests/tables.py 2015-03-19 19:07:29.000000000 +0000 @@ -1192,7 +1192,7 @@ res = http.HttpResponse(table.render()) self.assertContains(res, "multi_select_column hidden") - def test_table_action_object_display_is_none(self): + def test_table_action_object_display_is_id(self): action_string = "my_table__toggle__1" req = self.factory.post('/my_url/', {'action': action_string}) self.table = MyTable(req, TEST_DATA) @@ -1206,7 +1206,7 @@ handled = self.table.maybe_handle() self.assertEqual(302, handled.status_code) self.assertEqual("/my_url/", handled["location"]) - self.assertEqual(u"Downed Item: N/A", + self.assertEqual(u"Downed Item: 1", list(req._messages)[0].message) def test_table_column_can_be_selected(self): diff -Nru horizon-2015.1~b2/horizon/test/tests/utils.py horizon-2015.1~b3/horizon/test/tests/utils.py --- horizon-2015.1~b2/horizon/test/tests/utils.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/test/tests/utils.py 2015-03-19 19:07:23.000000000 +0000 @@ -247,7 +247,7 @@ # Key file only be read/writable by user: self.assertEqual("0600", oct(os.stat(key_file).st_mode & 0o777)) - os.chmod(key_file, 0o777) + os.chmod(key_file, 0o644) self.assertRaises(secret_key.FilePermissionError, secret_key.generate_or_read_from_file, key_file) os.remove(key_file) diff -Nru horizon-2015.1~b2/horizon/test/tests/views.py horizon-2015.1~b3/horizon/test/tests/views.py --- horizon-2015.1~b2/horizon/test/tests/views.py 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/horizon/test/tests/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,102 @@ +# 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 horizon.test import helpers as test +from horizon import views + +from django import forms +from django.test import client +from django.utils.translation import ugettext_lazy as _ +from django.views import generic + +FAKENAME = "FakeName" + + +class ViewData(object): + + template_name = 'fake' + + def get_context_data(self, **kwargs): + context = super(ViewData, self).get_context_data(**kwargs) + context['object'] = {'name': 'myName'} + return context + + +class PageWithNoTitle(ViewData, views.HorizonTemplateView): + pass + + +class PageWithTitle(ViewData, views.HorizonTemplateView): + page_title = "A Title" + + +class PageWithTitleData(ViewData, views.HorizonTemplateView): + page_title = "A Title: {{ object.name }}" + + +class FormWithTitle(ViewData, views.HorizonFormView): + form_class = forms.Form + page_title = "A Title: {{ object.name }}" + + +class ViewWithTitle(views.PageTitleMixin, generic.TemplateView): + page_title = "Fake" + + +class ViewWithTransTitle(views.PageTitleMixin, generic.TemplateView): + page_title = _("Fake") + + +class PageTitleTests(test.TestCase): + + def setUp(self): + super(PageTitleTests, self).setUp() + self.request = client.RequestFactory().get('fake') + + def _dispatch(self, viewClass): + p = viewClass() + p.request = self.request + return p.dispatch(self.request) + + def test_render_context_with_title(self): + tm = ViewWithTitle() + context = tm.render_context_with_title({}) + self.assertEqual("Fake", context['page_title']) + + def test_render_context_with_title_override(self): + tm = ViewWithTitle() + context = tm.render_context_with_title({'page_title': "ekaF"}) + self.assertEqual("ekaF", context['page_title']) + + def test_render_context_with_title_lazy_translations(self): + tm = ViewWithTransTitle() + context = tm.render_context_with_title({}) + self.assertEqual("Fake", context['page_title']) + + def test_no_title_set(self): + res = self._dispatch(PageWithNoTitle) + self.assertEqual("", res.context_data['page_title']) + + def test_title_set(self): + res = self._dispatch(PageWithTitle) + self.assertEqual("A Title", res.context_data['page_title']) + + def test_title_with_data(self): + res = self._dispatch(PageWithTitleData) + self.assertEqual("A Title: myName", res.context_data['page_title']) + + def test_form_with_title(self): + res = self._dispatch(FormWithTitle) + self.assertEqual("A Title: myName", res.context_data['page_title']) diff -Nru horizon-2015.1~b2/horizon/utils/functions.py horizon-2015.1~b3/horizon/utils/functions.py --- horizon-2015.1~b2/horizon/utils/functions.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/utils/functions.py 2015-03-19 19:07:23.000000000 +0000 @@ -14,6 +14,8 @@ import math import re +from oslo_utils import units + from django.conf import settings from django.contrib.auth import logout # noqa from django import http @@ -32,7 +34,7 @@ def bytes_to_gigabytes(bytes): # Converts the number of bytes to the next highest number of Gigabytes # For example 5000000 (5 Meg) would return '1' - return int(math.ceil(float(bytes) / 1024 ** 3)) + return int(math.ceil(float(bytes) / units.Gi)) def add_logout_reason(request, response, reason): diff -Nru horizon-2015.1~b2/horizon/utils/validators.py horizon-2015.1~b3/horizon/utils/validators.py --- horizon-2015.1~b2/horizon/utils/validators.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/utils/validators.py 2015-03-19 19:07:23.000000000 +0000 @@ -12,7 +12,10 @@ # License for the specific language governing permissions and limitations # under the License. +import re + from django.core.exceptions import ValidationError # noqa +from django.core import validators # noqa from django.utils.translation import ugettext_lazy as _ from horizon import conf @@ -47,3 +50,11 @@ raise ValidationError(_("Not a valid port number")) except ValueError: raise ValidationError(_("Port number must be integer")) + +# Same as POSIX [:print:]. Accordingly, diacritics are disallowed. +PRINT_REGEX = re.compile(r'^[\x20-\x7E]*$') + +validate_printable_ascii = validators.RegexValidator( + PRINT_REGEX, + _("The string may only contain ASCII printable characters."), + "invalid_characters") diff -Nru horizon-2015.1~b2/horizon/views.py horizon-2015.1~b3/horizon/views.py --- horizon-2015.1~b2/horizon/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/horizon/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -13,18 +13,72 @@ # under the License. from django import shortcuts +from django import template +from django.utils import encoding from django.views import generic import horizon from horizon import exceptions +class PageTitleMixin(object): + """A mixin that renders out a page title into a view. + + Many views in horizon have a page title that would ordinarily be + defined and passed through in get_context_data function, this often + leads to a lot of duplicated work in each view. + + This mixin standardises the process of defining a page title, letting + views simply define a variable that is rendered into the context for + them. + + There are cases when page title in a view may also display some context + data, for that purpose the page_title variable supports the django + templating language and will be rendered using the context defined by the + views get_context_data. + """ + + page_title = "" + + def render_context_with_title(self, context): + """This function takes in a context dict and uses it to render the + page_title variable, it then appends this title to the context using + the 'page_title' key. If there is already a page_title key defined in + context received then this function will do nothing. + """ + + if "page_title" not in context: + con = template.Context(context) + # NOTE(sambetts): Use force_text to ensure lazy translations + # are handled correctly. + temp = template.Template(encoding.force_text(self.page_title)) + context["page_title"] = temp.render(con) + return context + + def render_to_response(self, context): + """This is an override of the default render_to_response function that + exists in the django generic views, this is here to inject the + page title into the context before the main template is rendered. + """ + + context = self.render_context_with_title(context) + return super(PageTitleMixin, self).render_to_response(context) + + +class HorizonTemplateView(PageTitleMixin, generic.TemplateView): + pass + + +class HorizonFormView(PageTitleMixin, generic.FormView): + pass + + def user_home(request): """Reversible named view to direct a user to the appropriate homepage.""" return shortcuts.redirect(horizon.get_user_home(request.user)) -class APIView(generic.TemplateView): +class APIView(HorizonTemplateView): """A quick class-based view for putting API data into a template. Subclasses must define one method, ``get_data``, and a template name @@ -34,6 +88,7 @@ the :func:`horizon.exceptions.handle` error handler if not otherwise caught. """ + def get_data(self, request, context, *args, **kwargs): """This method should handle any necessary API calls, update the context object, and return the context object at the end. diff -Nru horizon-2015.1~b2/horizon.egg-info/pbr.json horizon-2015.1~b3/horizon.egg-info/pbr.json --- horizon-2015.1~b2/horizon.egg-info/pbr.json 2015-02-05 15:53:46.000000000 +0000 +++ horizon-2015.1~b3/horizon.egg-info/pbr.json 2015-03-19 19:09:09.000000000 +0000 @@ -1 +1 @@ -{"is_release": true, "git_version": "9d32c18"} \ No newline at end of file +{"is_release": true, "git_version": "a174b42"} \ No newline at end of file diff -Nru horizon-2015.1~b2/horizon.egg-info/PKG-INFO horizon-2015.1~b3/horizon.egg-info/PKG-INFO --- horizon-2015.1~b2/horizon.egg-info/PKG-INFO 2015-02-05 15:53:46.000000000 +0000 +++ horizon-2015.1~b3/horizon.egg-info/PKG-INFO 2015-03-19 19:09:09.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: horizon -Version: 2015.1.0b2 +Version: 2015.1.0b3 Summary: OpenStack Dashboard Home-page: http://www.openstack.org/ Author: OpenStack diff -Nru horizon-2015.1~b2/horizon.egg-info/requires.txt horizon-2015.1~b3/horizon.egg-info/requires.txt --- horizon-2015.1~b2/horizon.egg-info/requires.txt 2015-02-05 15:53:46.000000000 +0000 +++ horizon-2015.1~b3/horizon.egg-info/requires.txt 2015-03-19 19:09:09.000000000 +0000 @@ -11,7 +11,7 @@ kombu>=2.5.0 netaddr>=0.7.12 oslo.concurrency>=1.4.1 # Apache-2.0 -oslo.config>=1.6.0 # Apache-2.0 +oslo.config>=1.9.0 # Apache-2.0 oslo.i18n>=1.3.0 # Apache-2.0 oslo.serialization>=1.2.0 # Apache-2.0 oslo.utils>=1.2.0 # Apache-2.0 @@ -19,19 +19,20 @@ python-ceilometerclient>=1.0.6 python-cinderclient>=1.1.0 python-glanceclient>=0.15.0 -python-heatclient>=0.2.9 -python-keystoneclient>=1.0.0 -python-neutronclient>=2.3.6,<3 -python-novaclient>=2.18.0 +python-heatclient>=0.3.0 +python-keystoneclient>=1.1.0 +python-neutronclient>=2.3.11,<3 +python-novaclient>=2.18.0,!=2.21.0 python-saharaclient>=0.7.6 python-swiftclient>=2.2.0 python-troveclient>=1.0.7 pytz>=2013.6 PyYAML>=3.1.0 -six>=1.7.0 +six>=1.9.0 XStatic>=1.0.0 # MIT License XStatic-Angular>=1.3.7 # MIT License XStatic-Angular-Bootstrap>=0.11.0.2 # MIT License +XStatic-Angular-Irdragndrop>=1.0.2.1 # MIT License XStatic-Bootstrap-Datepicker>=1.3.1.0 # Apache 2.0 License XStatic-Bootstrap-SCSS>=3 # Apache 2.0 License XStatic-D3>=3.1.6.2 # BSD License3 clause @@ -48,3 +49,4 @@ XStatic-Rickshaw>=1.5.0 # BSD Licenseprior XStatic-smart-table>=1.4.5.3 # MIT License XStatic-Spin>=1.2.5.2 # MIT License +XStatic-term.js>=0.0.4 # MIT License diff -Nru horizon-2015.1~b2/horizon.egg-info/SOURCES.txt horizon-2015.1~b3/horizon.egg-info/SOURCES.txt --- horizon-2015.1~b2/horizon.egg-info/SOURCES.txt 2015-02-05 15:53:49.000000000 +0000 +++ horizon-2015.1~b3/horizon.egg-info/SOURCES.txt 2015-03-19 19:09:12.000000000 +0000 @@ -1,3 +1,4 @@ +.jshintrc .mailmap .pylintrc AUTHORS @@ -49,6 +50,7 @@ doc/source/topics/install.rst doc/source/topics/policy.rst doc/source/topics/settings.rst +doc/source/topics/table_actions.rst doc/source/topics/tables.rst doc/source/topics/testing.rst doc/source/topics/tutorial.rst @@ -156,10 +158,63 @@ horizon/management/commands/startpanel.py horizon/static/angular/styles.scss horizon/static/angular/widget.module.js +horizon/static/angular/action-list/action-list.js +horizon/static/angular/action-list/action-list.scss +horizon/static/angular/action-list/action-list.spec.js +horizon/static/angular/action-list/action.html +horizon/static/angular/action-list/button-tooltip.js +horizon/static/angular/action-list/menu-item.html +horizon/static/angular/action-list/menu.html +horizon/static/angular/action-list/single-button.html +horizon/static/angular/action-list/split-button.html +horizon/static/angular/action-list/warning-tooltip.html +horizon/static/angular/bind-scope/bind-scope.js +horizon/static/angular/bind-scope/bind-scope.spec.js +horizon/static/angular/charts/chart-tooltip.html +horizon/static/angular/charts/chart-tooltip.js +horizon/static/angular/charts/chart-tooltip.scss +horizon/static/angular/charts/charts.js +horizon/static/angular/charts/pie-chart.html +horizon/static/angular/charts/pie-chart.js +horizon/static/angular/charts/pie-chart.scss +horizon/static/angular/charts/pie-chart.spec.js +horizon/static/angular/form/form.js +horizon/static/angular/form/form.spec.js horizon/static/angular/help-panel/help-panel.html horizon/static/angular/help-panel/help-panel.js horizon/static/angular/help-panel/help-panel.scss horizon/static/angular/help-panel/help-panel.spec.js +horizon/static/angular/metadata-display/metadata-display.html +horizon/static/angular/metadata-display/metadata-display.js +horizon/static/angular/metadata-display/metadata-display.scss +horizon/static/angular/metadata-display/metadata-display.spec.js +horizon/static/angular/metadata-tree/metadata-tree-item.html +horizon/static/angular/metadata-tree/metadata-tree-service.js +horizon/static/angular/metadata-tree/metadata-tree.html +horizon/static/angular/metadata-tree/metadata-tree.js +horizon/static/angular/metadata-tree/metadata-tree.scss +horizon/static/angular/metadata-tree/metadata-tree.spec.js +horizon/static/angular/modal/modal.js +horizon/static/angular/modal/simple-modal.html +horizon/static/angular/modal/simple-modal.spec.js +horizon/static/angular/modal-wait-spinner/modal-wait-spinner.js +horizon/static/angular/modal-wait-spinner/modal-wait-spinner.scss +horizon/static/angular/table/basic-table.js +horizon/static/angular/table/basic-table.spec.js +horizon/static/angular/table/search-bar.html +horizon/static/angular/table/table.js +horizon/static/angular/table/table.scss +horizon/static/angular/table/table.spec.js +horizon/static/angular/transfer-table/allocated.html.example +horizon/static/angular/transfer-table/available.html.example +horizon/static/angular/transfer-table/transfer-table.html +horizon/static/angular/transfer-table/transfer-table.js +horizon/static/angular/transfer-table/transfer-table.scss +horizon/static/angular/transfer-table/transfer-table.spec.js +horizon/static/angular/wizard/wizard.html +horizon/static/angular/wizard/wizard.js +horizon/static/angular/wizard/wizard.scss +horizon/static/angular/wizard/wizard.spec.js horizon/static/horizon/js/horizon.accordion_nav.js horizon/static/horizon/js/horizon.communication.js horizon/static/horizon/js/horizon.d3barchart.js @@ -186,11 +241,21 @@ horizon/static/horizon/js/angular/horizon.js horizon/static/horizon/js/angular/hz.api.module.js horizon/static/horizon/js/angular/controllers/dummy.js -horizon/static/horizon/js/angular/controllers/metadata-widget-controller.js +horizon/static/horizon/js/angular/controllers/modal-form-update-metadata-ctrl.js horizon/static/horizon/js/angular/controllers/namespace-controller.js horizon/static/horizon/js/angular/directives/forms.js +horizon/static/horizon/js/angular/directives/serialConsole.js +horizon/static/horizon/js/angular/filters/filters.js +horizon/static/horizon/js/angular/filters/filters.spec.js horizon/static/horizon/js/angular/services/horizon.utils.js +horizon/static/horizon/js/angular/services/hz.api.cinder.js +horizon/static/horizon/js/angular/services/hz.api.config.js +horizon/static/horizon/js/angular/services/hz.api.glance.js horizon/static/horizon/js/angular/services/hz.api.keystone.js +horizon/static/horizon/js/angular/services/hz.api.neutron.js +horizon/static/horizon/js/angular/services/hz.api.nova.js +horizon/static/horizon/js/angular/services/hz.api.policy.js +horizon/static/horizon/js/angular/services/hz.api.security-group.js horizon/static/horizon/js/angular/services/hz.api.service.js horizon/static/horizon/js/angular/services/hz.api.service.spec.js horizon/static/horizon/lib/jquery/jquery.bootstrap.wizard.js @@ -199,7 +264,6 @@ horizon/static/horizon/tests/modals.js horizon/static/horizon/tests/tables.js horizon/static/horizon/tests/templates.js -horizon/static/horizon/tests/jasmine/metadataWidgetControllerSpec.js horizon/static/horizon/tests/jasmine/utilsSpec.js horizon/tables/__init__.py horizon/tables/actions.py @@ -342,6 +406,7 @@ horizon/test/tests/tabs.py horizon/test/tests/templatetags.py horizon/test/tests/utils.py +horizon/test/tests/views.py horizon/test/tests/workflows.py horizon/utils/__init__.py horizon/utils/csvbase.py @@ -361,6 +426,7 @@ openstack_dashboard/exceptions.py openstack_dashboard/hooks.py openstack_dashboard/policy.py +openstack_dashboard/policy_backend.py openstack_dashboard/settings.py openstack_dashboard/static_settings.py openstack_dashboard/urls.py @@ -383,9 +449,17 @@ openstack_dashboard/api/trove.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/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/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 @@ -453,8 +527,10 @@ 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 @@ -548,6 +624,7 @@ 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/_detail_overview.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 @@ -576,7 +653,6 @@ openstack_dashboard/dashboards/admin/routers/ports/urls.py openstack_dashboard/dashboards/admin/routers/ports/views.py openstack_dashboard/dashboards/admin/routers/templates/routers/_detail_overview.html -openstack_dashboard/dashboards/admin/routers/templates/routers/_update.html openstack_dashboard/dashboards/admin/routers/templates/routers/detail.html openstack_dashboard/dashboards/admin/routers/templates/routers/index.html openstack_dashboard/dashboards/admin/routers/templates/routers/update.html @@ -687,6 +763,7 @@ 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 @@ -710,9 +787,11 @@ 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 @@ -830,6 +909,7 @@ openstack_dashboard/dashboards/project/data_processing/clusters/templates/data_processing.clusters/_details.html openstack_dashboard/dashboards/project/data_processing/clusters/templates/data_processing.clusters/_instances_details.html openstack_dashboard/dashboards/project/data_processing/clusters/templates/data_processing.clusters/_nodegroups_details.html +openstack_dashboard/dashboards/project/data_processing/clusters/templates/data_processing.clusters/_rich_status.html openstack_dashboard/dashboards/project/data_processing/clusters/templates/data_processing.clusters/clusters.html openstack_dashboard/dashboards/project/data_processing/clusters/templates/data_processing.clusters/configure.html openstack_dashboard/dashboards/project/data_processing/clusters/templates/data_processing.clusters/create.html @@ -875,7 +955,6 @@ openstack_dashboard/dashboards/project/data_processing/data_sources/templates/data_processing.data_sources/_details.html openstack_dashboard/dashboards/project/data_processing/data_sources/templates/data_processing.data_sources/create.html openstack_dashboard/dashboards/project/data_processing/data_sources/templates/data_processing.data_sources/data_sources.html -openstack_dashboard/dashboards/project/data_processing/data_sources/templates/data_processing.data_sources/data_sources_form_script.html openstack_dashboard/dashboards/project/data_processing/data_sources/templates/data_processing.data_sources/details.html openstack_dashboard/dashboards/project/data_processing/data_sources/workflows/__init__.py openstack_dashboard/dashboards/project/data_processing/data_sources/workflows/create.py @@ -948,6 +1027,21 @@ openstack_dashboard/dashboards/project/data_processing/utils/helpers.py openstack_dashboard/dashboards/project/data_processing/utils/neutron_support.py openstack_dashboard/dashboards/project/data_processing/utils/workflow_helpers.py +openstack_dashboard/dashboards/project/data_processing/wizard/__init__.py +openstack_dashboard/dashboards/project/data_processing/wizard/forms.py +openstack_dashboard/dashboards/project/data_processing/wizard/panel.py +openstack_dashboard/dashboards/project/data_processing/wizard/tests.py +openstack_dashboard/dashboards/project/data_processing/wizard/urls.py +openstack_dashboard/dashboards/project/data_processing/wizard/views.py +openstack_dashboard/dashboards/project/data_processing/wizard/templates/data_processing.wizard/_job_type_select.html +openstack_dashboard/dashboards/project/data_processing/wizard/templates/data_processing.wizard/_job_type_select_help.html +openstack_dashboard/dashboards/project/data_processing/wizard/templates/data_processing.wizard/_plugin_select.html +openstack_dashboard/dashboards/project/data_processing/wizard/templates/data_processing.wizard/_plugin_select_help.html +openstack_dashboard/dashboards/project/data_processing/wizard/templates/data_processing.wizard/cluster_guide.html +openstack_dashboard/dashboards/project/data_processing/wizard/templates/data_processing.wizard/job_type_select.html +openstack_dashboard/dashboards/project/data_processing/wizard/templates/data_processing.wizard/jobex_guide.html +openstack_dashboard/dashboards/project/data_processing/wizard/templates/data_processing.wizard/plugin_select.html +openstack_dashboard/dashboards/project/data_processing/wizard/templates/data_processing.wizard/wizard.html openstack_dashboard/dashboards/project/database_backups/__init__.py openstack_dashboard/dashboards/project/database_backups/panel.py openstack_dashboard/dashboards/project/database_backups/tables.py @@ -974,11 +1068,11 @@ openstack_dashboard/dashboards/project/databases/templates/databases/_detail_overview_mongodb.html openstack_dashboard/dashboards/project/databases/templates/databases/_detail_overview_mysql.html openstack_dashboard/dashboards/project/databases/templates/databases/_detail_overview_redis.html +openstack_dashboard/dashboards/project/databases/templates/databases/_launch_advanced_help.html openstack_dashboard/dashboards/project/databases/templates/databases/_launch_details_help.html openstack_dashboard/dashboards/project/databases/templates/databases/_launch_initialize_help.html openstack_dashboard/dashboards/project/databases/templates/databases/_launch_network_help.html openstack_dashboard/dashboards/project/databases/templates/databases/_launch_networks.html -openstack_dashboard/dashboards/project/databases/templates/databases/_launch_restore_help.html openstack_dashboard/dashboards/project/databases/templates/databases/_resize_instance.html openstack_dashboard/dashboards/project/databases/templates/databases/_resize_volume.html openstack_dashboard/dashboards/project/databases/templates/databases/detail.html @@ -1074,6 +1168,7 @@ openstack_dashboard/dashboards/project/instances/templates/instances/detail.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 @@ -1231,6 +1326,9 @@ 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 @@ -1241,6 +1339,9 @@ openstack_dashboard/dashboards/project/stacks/templates/stacks/create.html openstack_dashboard/dashboards/project/stacks/templates/stacks/detail.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/resource.html openstack_dashboard/dashboards/project/stacks/templates/stacks/select_template.html openstack_dashboard/dashboards/project/stacks/templates/stacks/update.html @@ -1249,6 +1350,7 @@ 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 @@ -1394,6 +1496,7 @@ openstack_dashboard/locale/fr/LC_MESSAGES/django.po openstack_dashboard/locale/hi/LC_MESSAGES/django.po openstack_dashboard/locale/ja/LC_MESSAGES/django.po +openstack_dashboard/locale/kn/LC_MESSAGES/django.po openstack_dashboard/locale/ko_KR/LC_MESSAGES/django.po openstack_dashboard/locale/nl_NL/LC_MESSAGES/django.po openstack_dashboard/locale/pl_PL/LC_MESSAGES/django.po @@ -1407,6 +1510,7 @@ 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/openstack/__init__.py openstack_dashboard/openstack/common/__init__.py openstack_dashboard/openstack/common/_i18n.py @@ -1414,6 +1518,9 @@ openstack_dashboard/openstack/common/local.py openstack_dashboard/openstack/common/log.py openstack_dashboard/openstack/common/policy.py +openstack_dashboard/static/dashboard/dashboard.module.js +openstack_dashboard/static/dashboard/dashboard.module.spec.js +openstack_dashboard/static/dashboard/dashboard.scss openstack_dashboard/static/dashboard/fonts/Anivers_Regular-webfont.eot openstack_dashboard/static/dashboard/fonts/Anivers_Regular-webfont.svg openstack_dashboard/static/dashboard/fonts/Anivers_Regular-webfont.ttf @@ -1453,8 +1560,41 @@ openstack_dashboard/static/dashboard/img/unknown-green.svg openstack_dashboard/static/dashboard/img/unknown-red.svg openstack_dashboard/static/dashboard/img/up_arrow.png +openstack_dashboard/static/dashboard/launch-instance/launch-instance.js +openstack_dashboard/static/dashboard/launch-instance/launch-instance.model.js +openstack_dashboard/static/dashboard/launch-instance/launch-instance.model.spec.js +openstack_dashboard/static/dashboard/launch-instance/launch-instance.scss +openstack_dashboard/static/dashboard/launch-instance/launch-instance.spec.js +openstack_dashboard/static/dashboard/launch-instance/access-and-security/access-and-security.help.html +openstack_dashboard/static/dashboard/launch-instance/access-and-security/access-and-security.html +openstack_dashboard/static/dashboard/launch-instance/access-and-security/access-and-security.js +openstack_dashboard/static/dashboard/launch-instance/access-and-security/access-and-security.scss +openstack_dashboard/static/dashboard/launch-instance/access-and-security/access-and-security.spec.js +openstack_dashboard/static/dashboard/launch-instance/access-and-security/keypair-details.html +openstack_dashboard/static/dashboard/launch-instance/access-and-security/security-group-details.html +openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.help.html +openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.html +openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.js +openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.scss +openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.spec.js +openstack_dashboard/static/dashboard/launch-instance/flavor/flavor.help.html +openstack_dashboard/static/dashboard/launch-instance/flavor/flavor.html +openstack_dashboard/static/dashboard/launch-instance/flavor/flavor.js +openstack_dashboard/static/dashboard/launch-instance/flavor/flavor.scss +openstack_dashboard/static/dashboard/launch-instance/flavor/flavor.spec.js +openstack_dashboard/static/dashboard/launch-instance/network/network.help.html +openstack_dashboard/static/dashboard/launch-instance/network/network.html +openstack_dashboard/static/dashboard/launch-instance/network/network.js +openstack_dashboard/static/dashboard/launch-instance/network/network.scss +openstack_dashboard/static/dashboard/launch-instance/network/network.spec.js +openstack_dashboard/static/dashboard/launch-instance/source/source.help.html +openstack_dashboard/static/dashboard/launch-instance/source/source.html +openstack_dashboard/static/dashboard/launch-instance/source/source.js +openstack_dashboard/static/dashboard/launch-instance/source/source.scss +openstack_dashboard/static/dashboard/launch-instance/source/source.spec.js openstack_dashboard/static/dashboard/scss/_variables.scss openstack_dashboard/static/dashboard/scss/horizon.scss +openstack_dashboard/static/dashboard/scss/serial_console.css openstack_dashboard/static/dashboard/scss/components/_accordion_nav.scss openstack_dashboard/static/dashboard/scss/components/_charts.scss openstack_dashboard/static/dashboard/scss/components/_inline_edit.scss @@ -1471,26 +1611,35 @@ 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/jasmine/index.html +openstack_dashboard/templates/jasmine/jasmine.html openstack_dashboard/templatetags/__init__.py openstack_dashboard/templatetags/context_selection.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_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/rest_test_utils.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_tests.py openstack_dashboard/test/api_tests/vpnaas_tests.py @@ -1513,6 +1662,7 @@ 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/identity/__init__.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 @@ -1521,6 +1671,8 @@ openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/keypairspage.py openstack_dashboard/test/integration_tests/pages/project/compute/volumes/__init__.py openstack_dashboard/test/integration_tests/pages/project/data_processing/__init__.py +openstack_dashboard/test/integration_tests/pages/project/data_processing/imageregistrypage.py +openstack_dashboard/test/integration_tests/pages/project/data_processing/jobbinariespage.py openstack_dashboard/test/integration_tests/pages/project/network/__init__.py openstack_dashboard/test/integration_tests/pages/project/object_store/__init__.py openstack_dashboard/test/integration_tests/pages/project/orchestration/__init__.py @@ -1543,7 +1695,13 @@ openstack_dashboard/test/integration_tests/tests/test_keypair.py openstack_dashboard/test/integration_tests/tests/test_login.py openstack_dashboard/test/integration_tests/tests/test_password_change.py +openstack_dashboard/test/integration_tests/tests/test_sahara_image_registry.py +openstack_dashboard/test/integration_tests/tests/test_sahara_job_binaries.py +openstack_dashboard/test/integration_tests/tests/test_user_create_delete.py openstack_dashboard/test/integration_tests/tests/test_user_settings.py +openstack_dashboard/test/jasmine/__init__.py +openstack_dashboard/test/jasmine/jasmine.py +openstack_dashboard/test/jasmine/jasmine_tests.py openstack_dashboard/test/templates/404.html openstack_dashboard/test/templates/500.html openstack_dashboard/test/templates/_tab.html @@ -1584,6 +1742,7 @@ 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 diff -Nru horizon-2015.1~b2/.jshintrc horizon-2015.1~b3/.jshintrc --- horizon-2015.1~b2/.jshintrc 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/.jshintrc 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,33 @@ +{ + "browser": true, + "trailing": true, + "evil": true, + + "globals": { + "jQuery": false, + "$": false, + + "angular": false, + "module": false, + "inject": false, + + "describe": false, + "beforeEach": false, + "afterEach": false, + "beforeAll": false, + "afterAll": false, + "it": false, + "expect": false, + + "d3": false, + + "pluralidx": false, + "gettext": false, + "ngettext": false, + "gettext_noop": false, + "pgettext": false, + "npgettext": false, + "interpolate": false, + "get_format": false + } +} diff -Nru horizon-2015.1~b2/openstack_dashboard/api/base.py horizon-2015.1~b3/openstack_dashboard/api/base.py --- horizon-2015.1~b2/openstack_dashboard/api/base.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/base.py 2015-03-19 19:07:23.000000000 +0000 @@ -23,6 +23,8 @@ from horizon import exceptions +import six + __all__ = ('APIResourceWrapper', 'APIDictWrapper', 'get_service_from_catalog', 'url_for',) @@ -41,6 +43,13 @@ self.preferred = preferred_version self._active = None self.supported = {} + # As a convenience, we can drop in a placeholder for APIs that we + # have not yet needed to version. This is useful, for example, when + # panels such as the admin metadata_defs wants to check the active + # version even though it's not explicitly defined. Previously + # this caused a KeyError. + if self.preferred: + self.supported[self.preferred] = {"version": self.preferred} @property def active(self): @@ -60,9 +69,26 @@ # the setting in as a way of overriding the latest available # version. key = self.preferred + # Since we do a key lookup in the supported dict the type matters, + # let's ensure people know if they use a string when the key isn't. + if isinstance(key, six.string_types): + msg = ('The version "%s" specified for the %s service should be ' + 'either an integer or a float, not a string.' % + (key, self.service_type)) + raise exceptions.ConfigurationError(msg) + # Provide a helpful error message if the specified version isn't in the + # supported list. + if key not in self.supported: + choices = ", ".join(str(k) for k in six.iterkeys(self.supported)) + msg = ('%s is not a supported API version for the %s service, ' + ' choices are: %s' % (key, self.service_type, choices)) + raise exceptions.ConfigurationError(msg) self._active = key return self.supported[self._active] + def clear_active_cache(self): + self._active = None + class APIResourceWrapper(object): """Simple wrapper for api objects. @@ -91,6 +117,12 @@ for attr in self._attrs if hasattr(self, attr))) + def to_dict(self): + obj = {} + for key in self._attrs: + obj[key] = getattr(self._apiresource, key, None) + return obj + class APIDictWrapper(object): """Simple wrapper for api dictionaries @@ -138,6 +170,9 @@ def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self._apidict) + def to_dict(self): + return self._apidict + class Quota(object): """Wrapper for individual limits in a quota.""" @@ -154,7 +189,7 @@ into Quota objects for easier handling/iteration. `QuotaSet` objects support a mix of `list` and `dict` methods; you can use - the bracket notiation (`qs["my_quota"] = 0`) to add new quota values, and + the bracket notation (`qs["my_quota"] = 0`) to add new quota values, and use the `get` method to retrieve a specific quota, but otherwise it behaves much like a list or tuple, particularly in supporting iteration. """ diff -Nru horizon-2015.1~b2/openstack_dashboard/api/ceilometer.py horizon-2015.1~b3/openstack_dashboard/api/ceilometer.py --- horizon-2015.1~b2/openstack_dashboard/api/ceilometer.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/ceilometer.py 2015-03-19 19:07:23.000000000 +0000 @@ -48,7 +48,7 @@ def make_query(user_id=None, tenant_id=None, resource_id=None, user_ids=None, tenant_ids=None, resource_ids=None): - """Returns query built form given parameters. + """Returns query built from given parameters. This query can be then used for querying resources, meters and statistics. @@ -86,8 +86,7 @@ class Meter(base.APIResourceWrapper): """Represents one Ceilometer meter.""" - _attrs = ['name', 'type', 'unit', 'resource_id', 'user_id', - 'project_id'] + _attrs = ['name', 'type', 'unit', 'resource_id', 'user_id', 'project_id'] def __init__(self, apiresource): super(Meter, self).__init__(apiresource) @@ -187,13 +186,13 @@ class ResourceAggregate(Resource): """Represents aggregate of more resources together. - Aggregate of resources can be obtain by specifing + Aggregate of resources can be obtained by specifying multiple ids in one parameter or by not specifying one parameter. - Or it can be specified by query directly. + It can also be specified by query directly. Example: - We obtain can have aggregate of resources by specifying + We can obtain an aggregate of resources by specifying multiple resource_ids in resource_id parameter in init. Or we can specify only tenant_id, which will return all resources of that tenant. @@ -217,19 +216,19 @@ else: # TODO(lsmola) make parallel obtaining of tenant and user # make the threading here, thread join into resource_list - if (ceilometer_usage and tenant_id): + if ceilometer_usage and tenant_id: self.tenant_id = tenant_id self._tenant = ceilometer_usage.get_tenant(tenant_id) else: self._tenant = None - if (ceilometer_usage and user_id): + if ceilometer_usage and user_id: self.user_id = user_id self._user = ceilometer_usage.get_user(user_id) else: self._user = None - if (resource_id): + if resource_id: self.resource_id = resource_id self._query = make_query(tenant_id=tenant_id, user_id=user_id, @@ -286,8 +285,7 @@ def resource_list(request, query=None, ceilometer_usage_object=None): """List the resources.""" - resources = ceilometerclient(request).\ - resources.list(q=query) + resources = ceilometerclient(request).resources.list(q=query) return [Resource(r, ceilometer_usage_object) for r in resources] @@ -402,10 +400,10 @@ self._tenants = {} def get_user(self, user_id): - """Returns user fetched form API + """Returns user fetched from API. Caching the result, so it doesn't contact API twice with the - same query + same query. """ user = self._users.get(user_id, None) @@ -418,7 +416,7 @@ def preload_all_users(self): """Preloads all users into dictionary. - It's more effective to preload all users, rather the fetching many + It's more effective to preload all users, rather than fetching many users by separate API get calls. """ @@ -429,10 +427,10 @@ self._users[u.id] = u def get_tenant(self, tenant_id): - """Returns tenant fetched form API. + """Returns tenant fetched from API. Caching the result, so it doesn't contact API twice with the - same query + same query. """ tenant = self._tenants.get(tenant_id, None) @@ -443,10 +441,10 @@ return tenant def preload_all_tenants(self): - """Preloads all teannts into dictionary. + """Preloads all tenants into dictionary. - It's more effective to preload all tenants, rather the fetching many - tenants by separate API get calls. + It's more effective to preload all tenants, rather than fetching each + tenant by separate API get calls. """ tenants, more = keystone.tenant_list(self._request) @@ -488,9 +486,9 @@ filter_func = None def filter_resources(resource): - """Method for filtering resources by theirs links.rel attr. + """Method for filtering resources by their links.rel attr. - The links.rel attributes contains all meters the resource have. + The links.rel attributes contain all meters the resource has. """ for link in resource.links: if link['rel'] in used_cls.meters: @@ -498,7 +496,7 @@ return False if not query: - # Not all resource types can be obtain by query, if there is not + # Not all resource types can be obtained by query, if there is not # a query, we are filtering all resources by this function. filter_func = filter_resources @@ -560,7 +558,7 @@ """ if not meter_names: - raise ValueError("meter_names and resource must be defined to be" + raise ValueError("meter_names and resources must be defined to be " "able to obtain the statistics.") # query for identifying one resource in meters @@ -719,7 +717,7 @@ class Meters(object): - """Class for listing of available meters + """Class for listing of available meters. It is listing meters defined in this class that are available in Ceilometer meter_list. @@ -742,7 +740,7 @@ except Exception: self._ceilometer_meter_list = [] exceptions.handle(self._request, - _('Unable to retrieve Ceilometer meter' + _('Unable to retrieve Ceilometer meter ' 'list.')) # Storing the meters info categorized by their services. @@ -773,71 +771,71 @@ self._cached_meters = {} def list_all(self, only_meters=None, except_meters=None): - """Returns a list of meters based on the meters names + """Returns a list of meters based on the meters names. :Parameters: - - `only_meters`: The list of meter_names we want to show - - `except_meters`: The list of meter names we don't want to show + - `only_meters`: The list of meter names we want to show. + - `except_meters`: The list of meter names we don't want to show. """ return self._list(only_meters=only_meters, except_meters=except_meters) def list_nova(self, except_meters=None): - """Returns a list of meters tied to nova + """Returns a list of meters tied to nova. :Parameters: - - `except_meters`: The list of meter names we don't want to show + - `except_meters`: The list of meter names we don't want to show. """ return self._list(only_meters=self._nova_meters_info.keys(), except_meters=except_meters) def list_neutron(self, except_meters=None): - """Returns a list of meters tied to neutron + """Returns a list of meters tied to neutron. :Parameters: - - `except_meters`: The list of meter names we don't want to show + - `except_meters`: The list of meter names we don't want to show. """ return self._list(only_meters=self._neutron_meters_info.keys(), except_meters=except_meters) def list_glance(self, except_meters=None): - """Returns a list of meters tied to glance + """Returns a list of meters tied to glance. :Parameters: - - `except_meters`: The list of meter names we don't want to show + - `except_meters`: The list of meter names we don't want to show. """ return self._list(only_meters=self._glance_meters_info.keys(), except_meters=except_meters) def list_cinder(self, except_meters=None): - """Returns a list of meters tied to cinder + """Returns a list of meters tied to cinder. :Parameters: - - `except_meters`: The list of meter names we don't want to show + - `except_meters`: The list of meter names we don't want to show. """ return self._list(only_meters=self._cinder_meters_info.keys(), except_meters=except_meters) def list_swift(self, except_meters=None): - """Returns a list of meters tied to swift + """Returns a list of meters tied to swift. :Parameters: - - `except_meters`: The list of meter names we don't want to show + - `except_meters`: The list of meter names we don't want to show. """ return self._list(only_meters=self._swift_meters_info.keys(), except_meters=except_meters) def list_kwapi(self, except_meters=None): - """Returns a list of meters tied to kwapi + """Returns a list of meters tied to kwapi. :Parameters: - - `except_meters`: The list of meter names we don't want to show + - `except_meters`: The list of meter names we don't want to show. """ return self._list(only_meters=self._kwapi_meters_info.keys(), @@ -854,11 +852,11 @@ except_meters=except_meters) def _list(self, only_meters=None, except_meters=None): - """Returns a list of meters based on the meters names + """Returns a list of meters based on the meters names. :Parameters: - - `only_meters`: The list of meter_names we want to show - - `except_meters`: The list of meter names we don't want to show + - `only_meters`: The list of meter names we want to show. + - `except_meters`: The list of meter names we don't want to show. """ # Get all wanted meter names. @@ -873,7 +871,7 @@ return self._get_meters(meter_names) def _get_meters(self, meter_names): - """Obtain meters based on meter_names + """Obtain meters based on meter_names. The meters that do not exist in Ceilometer meter list are left out. @@ -889,7 +887,7 @@ return meters def _get_meter(self, meter_name): - """Obtains a meter + """Obtains a meter. Obtains meter either from cache or from Ceilometer meter list joined with statically defined meter info like label and description. @@ -918,7 +916,7 @@ return meter def _get_nova_meters_info(self): - """Returns additional info for each meter + """Returns additional info for each meter. That will be used for augmenting the Ceilometer meter. """ @@ -930,20 +928,20 @@ meters_info = datastructures.SortedDict([ ("instance", { 'label': '', - 'description': _("Duration of instance"), + 'description': _("Existence of instance"), }), ("instance:", { 'label': '', - 'description': _("Duration of instance " + 'description': _("Existence of instance " "(openstack types)"), }), ("memory", { 'label': '', - 'description': _("Volume of RAM in MB"), + 'description': _("Volume of RAM"), }), ("memory.usage", { 'label': '', - 'description': _("Volume of RAM used in MB"), + 'description': _("Volume of RAM used"), }), ("cpu", { 'label': '', @@ -967,36 +965,35 @@ }), ("disk.read.bytes", { 'label': '', - 'description': _("Volume of reads in B"), + 'description': _("Volume of reads"), }), ("disk.write.bytes", { 'label': '', - 'description': _("Volume of writes in B"), + 'description': _("Volume of writes"), }), ("disk.read.requests.rate", { 'label': '', - 'description': _("Average rate of read requests per second"), + 'description': _("Average rate of read requests"), }), ("disk.write.requests.rate", { 'label': '', - 'description': _("Average rate of write requests per second"), + 'description': _("Average rate of write requests"), }), ("disk.read.bytes.rate", { 'label': '', - 'description': _("Average rate of reads in B per second"), + 'description': _("Average rate of reads"), }), ("disk.write.bytes.rate", { 'label': '', - 'description': _("Average volume of writes in B per second"), + 'description': _("Average volume of writes"), }), ("disk.root.size", { 'label': '', - 'description': _("Size of root disk in GB"), + 'description': _("Size of root disk"), }), ("disk.ephemeral.size", { 'label': '', - 'description': _("Size of ephemeral disk " - "in GB"), + 'description': _("Size of ephemeral disk"), }), ("network.incoming.bytes", { 'label': '', @@ -1055,9 +1052,9 @@ return meters_info def _get_neutron_meters_info(self): - """Returns additional info for each meter + """Returns additional info for each meter. - That will be used for augmenting the Ceilometer meter + That will be used for augmenting the Ceilometer meter. """ # TODO(lsmola) Unless the Ceilometer will provide the information @@ -1067,7 +1064,7 @@ return datastructures.SortedDict([ ('network', { 'label': '', - 'description': _("Duration of network"), + 'description': _("Existence of network"), }), ('network.create', { 'label': '', @@ -1079,7 +1076,7 @@ }), ('subnet', { 'label': '', - 'description': _("Duration of subnet"), + 'description': _("Existence of subnet"), }), ('subnet.create', { 'label': '', @@ -1091,7 +1088,7 @@ }), ('port', { 'label': '', - 'description': _("Duration of port"), + 'description': _("Existence of port"), }), ('port.create', { 'label': '', @@ -1103,7 +1100,7 @@ }), ('router', { 'label': '', - 'description': _("Duration of router"), + 'description': _("Existence of router"), }), ('router.create', { 'label': '', @@ -1115,7 +1112,7 @@ }), ('ip.floating', { 'label': '', - 'description': _("Duration of floating ip"), + 'description': _("Existence of floating ip"), }), ('ip.floating.create', { 'label': '', @@ -1128,9 +1125,9 @@ ]) def _get_glance_meters_info(self): - """Returns additional info for each meter + """Returns additional info for each meter. - That will be used for augmenting the Ceilometer meter + That will be used for augmenting the Ceilometer meter. """ # TODO(lsmola) Unless the Ceilometer will provide the information @@ -1148,15 +1145,15 @@ }), ('image.update', { 'label': '', - 'description': _("Number of update on the image"), + 'description': _("Number of image updates"), }), ('image.upload', { 'label': '', - 'description': _("Number of upload of the image"), + 'description': _("Number of image uploads"), }), ('image.delete', { 'label': '', - 'description': _("Number of delete on the image"), + 'description': _("Number of image deletions"), }), ('image.download', { 'label': '', @@ -1169,9 +1166,9 @@ ]) def _get_cinder_meters_info(self): - """Returns additional info for each meter + """Returns additional info for each meter. - That will be used for augmenting the Ceilometer meter + That will be used for augmenting the Ceilometer meter. """ # TODO(lsmola) Unless the Ceilometer will provide the information @@ -1181,7 +1178,7 @@ return datastructures.SortedDict([ ('volume', { 'label': '', - 'description': _("Duration of volume"), + 'description': _("Existence of volume"), }), ('volume.size', { 'label': '', @@ -1190,9 +1187,9 @@ ]) def _get_swift_meters_info(self): - """Returns additional info for each meter + """Returns additional info for each meter. - That will be used for augmenting the Ceilometer meter + That will be used for augmenting the Ceilometer meter. """ # TODO(lsmola) Unless the Ceilometer will provide the information @@ -1227,9 +1224,9 @@ ]) def _get_kwapi_meters_info(self): - """Returns additional info for each meter + """Returns additional info for each meter. - That will be used for augmenting the Ceilometer meter + That will be used for augmenting the Ceilometer meter. """ # TODO(lsmola) Unless the Ceilometer will provide the information diff -Nru horizon-2015.1~b2/openstack_dashboard/api/cinder.py horizon-2015.1~b3/openstack_dashboard/api/cinder.py --- horizon-2015.1~b2/openstack_dashboard/api/cinder.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/cinder.py 2015-03-19 19:07:29.000000000 +0000 @@ -72,6 +72,12 @@ return (getattr(self._apiresource, 'description', None) or getattr(self._apiresource, 'display_description', None)) + def to_dict(self): + obj = {} + for key in self._attrs: + obj[key] = getattr(self._apiresource, key, None) + return obj + class Volume(BaseCinderAPIResourceWrapper): @@ -207,8 +213,8 @@ instance = nova.server_get(request, attachment['server_id']) attachment['instance_name'] = instance.name else: - # Nova volume can occasionally send back error'd attachments - # the lack a server_id property; to work around that we'll + # Nova volume can occasionally send attachments in error state + # that lack a server_id property; to work around that we'll # give the attached instance a generic name. attachment['instance_name'] = _("Unknown instance") diff -Nru horizon-2015.1~b2/openstack_dashboard/api/fwaas.py horizon-2015.1~b3/openstack_dashboard/api/fwaas.py --- horizon-2015.1~b2/openstack_dashboard/api/fwaas.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/fwaas.py 2015-03-19 19:07:23.000000000 +0000 @@ -74,10 +74,22 @@ return _rule_list(request, expand_policy=True, **kwargs) +def rule_list_for_tenant(request, tenant_id, **kwargs): + """Return a rule list available for the tenant. + + The list contains rules owned by the tenant and shared rules. + This is required because Neutron returns all resources including + all tenants if a user has admin role. + """ + rules = rule_list(request, tenant_id=tenant_id, shared=False, **kwargs) + shared_rules = rule_list(request, shared=True, **kwargs) + return rules + shared_rules + + def _rule_list(request, expand_policy, **kwargs): rules = neutronclient(request).list_firewall_rules( **kwargs).get('firewall_rules') - if expand_policy: + if expand_policy and rules: policies = _policy_list(request, expand_rule=False) policy_dict = SortedDict((p.id, p) for p in policies) for rule in rules: @@ -133,10 +145,23 @@ return _policy_list(request, expand_rule=True, **kwargs) +def policy_list_for_tenant(request, tenant_id, **kwargs): + """Return a policy list available for the tenant. + + The list contains policies owned by the tenant and shared policies. + This is required because Neutron returns all resources including + all tenants if a user has admin role. + """ + policies = policy_list(request, tenant_id=tenant_id, + shared=False, **kwargs) + shared_policies = policy_list(request, shared=True, **kwargs) + return policies + shared_policies + + def _policy_list(request, expand_rule, **kwargs): policies = neutronclient(request).list_firewall_policies( **kwargs).get('firewall_policies') - if expand_rule: + if expand_rule and policies: rules = _rule_list(request, expand_policy=False) rule_dict = SortedDict((rule.id, rule) for rule in rules) for p in policies: @@ -206,10 +231,25 @@ return _firewall_list(request, expand_policy=True, **kwargs) +def firewall_list_for_tenant(request, tenant_id, **kwargs): + """Return a firewall list available for the tenant. + + The list contains firewalls owned by the tenant and shared firewalls. + This is required because Neutron returns all resources including + all tenants if a user has admin role. + """ + # NOTE(amotoki): At now 'shared' attribute is not visible in Neutron + # and there is no way to query shared firewalls explicitly. + # Thus this method returns the same as when tenant_id is specified, + # but I would like to have this method for symmetry to firewall + # rules and policies to avoid unnecessary confusion. + return firewall_list(request, tenant_id=tenant_id, **kwargs) + + def _firewall_list(request, expand_policy, **kwargs): firewalls = neutronclient(request).list_firewalls( **kwargs).get('firewalls') - if expand_policy: + if expand_policy and firewalls: policies = _policy_list(request, expand_rule=False) policy_dict = SortedDict((p.id, p) for p in policies) for fw in firewalls: diff -Nru horizon-2015.1~b2/openstack_dashboard/api/glance.py horizon-2015.1~b3/openstack_dashboard/api/glance.py --- horizon-2015.1~b2/openstack_dashboard/api/glance.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/glance.py 2015-03-19 19:07:29.000000000 +0000 @@ -168,6 +168,9 @@ result[attr] = getattr(self, attr) return json.dumps(result, indent=indent) + def to_dict(self): + return self._apiresource + class Namespace(BaseGlanceMetadefAPIResourceWrapper): @@ -194,7 +197,7 @@ namespace = glanceclient(request, '2').\ metadefs_namespace.get(namespace, resource_type=resource_type) # There were problems with using the wrapper class in - # in nested json serialization. So sometimes, it is not desirable + # nested json serialization. So sometimes, it is not desirable # to wrap. if wrap: return Namespace(namespace) @@ -222,7 +225,8 @@ typically at first deployment is done in a single transaction giving them a potentially unpredictable sort result when using create_at. - :param filters: specifies addition fields to filter on such as name. + :param filters: specifies addition fields to filter on such as + resource_types. :returns A tuple of three values: 1) Current page results 2) A boolean of whether or not there are previous page(s). diff -Nru horizon-2015.1~b2/openstack_dashboard/api/heat.py horizon-2015.1~b3/openstack_dashboard/api/heat.py --- horizon-2015.1~b2/openstack_dashboard/api/heat.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/heat.py 2015-03-19 19:07:23.000000000 +0000 @@ -102,6 +102,10 @@ return heatclient(request, password).stacks.create(**kwargs) +def stack_preview(request, password=None, **kwargs): + return heatclient(request, password).stacks.preview(**kwargs) + + def stack_update(request, stack_id, password=None, **kwargs): return heatclient(request, password).stacks.update(stack_id, **kwargs) @@ -144,3 +148,7 @@ def resource_type_get(request, resource_type): return heatclient(request).resource_types.get(resource_type) + + +def service_list(request): + return heatclient(request).services.list() diff -Nru horizon-2015.1~b2/openstack_dashboard/api/keystone.py horizon-2015.1~b3/openstack_dashboard/api/keystone.py --- horizon-2015.1~b2/openstack_dashboard/api/keystone.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/keystone.py 2015-03-19 19:07:23.000000000 +0000 @@ -255,7 +255,7 @@ def tenant_list(request, paginate=False, marker=None, domain=None, user=None, - admin=True): + admin=True, filters=None): manager = VERSIONS.get_project_manager(request, admin=admin) page_size = utils.get_page_size(request) @@ -270,7 +270,13 @@ tenants.pop(-1) has_more_data = True else: - tenants = manager.list(domain=domain, user=user) + kwargs = { + "domain": domain, + "user": user + } + if filters is not None: + kwargs.update(filters) + tenants = manager.list(**kwargs) return (tenants, has_more_data) @@ -284,7 +290,7 @@ enabled=enabled, domain=domain, **kwargs) -def user_list(request, project=None, domain=None, group=None): +def user_list(request, project=None, domain=None, group=None, filters=None): if VERSIONS.active < 3: kwargs = {"tenant_id": project} else: @@ -293,6 +299,8 @@ "domain": domain, "group": group } + if filters is not None: + kwargs.update(filters) users = keystoneclient(request, admin=True).users.list(**kwargs) return [VERSIONS.upgrade_v2_user(user) for user in users] @@ -329,9 +337,8 @@ raise keystone_exceptions.ClientException( 405, _("Identity service does not allow editing user data.")) - # The v2 API updates user model, password and default project separately + # The v2 API updates user model and default project separately if VERSIONS.active < 3: - password = data.pop('password') project = data.pop('project') # Update user details @@ -358,38 +365,15 @@ 'that project.') % data.get('name', None)) - # If present, update password - # FIXME(gabriel): password change should be its own form + view - if password: - try: - user_update_password(request, user, password) - if user.id == request.user.id: - return utils.logout_with_message( - request, - _("Password changed. Please log in again to " - "continue."), - redirect=False - ) - except Exception: - error = exceptions.handle(request, ignore=True) - if error is not None: raise error # v3 API is so much simpler... else: - if not data['password']: - data.pop('password') try: user = manager.update(user, **data) except keystone_exceptions.Conflict: raise exceptions.Conflict() - if data.get('password') and user.id == request.user.id: - return utils.logout_with_message( - request, - _("Password changed. Please log in again to continue."), - redirect=False - ) def user_update_enabled(request, user, enabled): @@ -401,6 +385,11 @@ def user_update_password(request, user, password, admin=True): + + if not keystone_can_edit_user(): + raise keystone_exceptions.ClientException( + 405, _("Identity service does not allow editing user password.")) + manager = keystoneclient(request, admin=admin).users if VERSIONS.active < 3: return manager.update_password(user, password) @@ -408,6 +397,27 @@ return manager.update(user, password=password) +def user_verify_admin_password(request, admin_password): + # attempt to create a new client instance with admin password to + # verify if it's correct. + client = keystone_client_v2 if VERSIONS.active < 3 else keystone_client_v3 + try: + endpoint = _get_endpoint_url(request, 'internalURL') + insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) + cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None) + client.Client( + username=request.user.username, + password=admin_password, + insecure=insecure, + cacert=cacert, + auth_url=endpoint + ) + return True + except Exception: + exceptions.handle(request, ignore=True) + return False + + def user_update_own_password(request, origpassword, password): client = keystoneclient(request, admin=False) client.user_id = request.user.id diff -Nru horizon-2015.1~b2/openstack_dashboard/api/neutron.py horizon-2015.1~b3/openstack_dashboard/api/neutron.py --- horizon-2015.1~b2/openstack_dashboard/api/neutron.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/neutron.py 2015-03-19 19:07:29.000000000 +0000 @@ -28,6 +28,7 @@ from django.conf import settings from django.utils.datastructures import SortedDict from django.utils.translation import ugettext_lazy as _ +from neutronclient.common import exceptions as neutron_exc from neutronclient.v2_0 import client as neutron_client from horizon import messages @@ -87,12 +88,17 @@ def __init__(self, apiresource): apiresource['admin_state'] = \ 'UP' if apiresource['admin_state_up'] else 'DOWN' - # Django cannot handle a key name with a colon, so remap another key + # Django cannot handle a key name with ':', so use '__' for key in apiresource.keys(): - if key.find(':'): + if ':' in key: apiresource['__'.join(key.split(':'))] = apiresource[key] super(Network, self).__init__(apiresource) + def to_dict(self): + d = dict(super(NeutronAPIDictWrapper, self).to_dict()) + d['subnets'] = [s.to_dict() for s in d['subnets']] + return d + class Subnet(NeutronAPIDictWrapper): """Wrapper for neutron subnets.""" @@ -106,6 +112,10 @@ """Wrapper for neutron ports.""" def __init__(self, apiresource): + # Django cannot handle a key name with ':', so use '__' + for key in apiresource.keys(): + if ':' in key: + apiresource['__'.join(key.split(':'))] = apiresource[key] apiresource['admin_state'] = \ 'UP' if apiresource['admin_state_up'] else 'DOWN' if 'mac_learning_enabled' in apiresource: @@ -142,6 +152,9 @@ for rule in sg['security_group_rules']] super(SecurityGroup, self).__init__(sg) + def to_dict(self): + return {k: self._apidict[k] for k in self._apidict if k != 'rules'} + class SecurityGroupRule(NeutronAPIDictWrapper): # Required attributes: @@ -420,7 +433,11 @@ if ((p.device_owner in ROUTER_INTERFACE_OWNERS) and (p.device_id in gw_routers))]) - return reachable_subnets + # we have to include any shared subnets as well because we may not + # have permission to see the router interface to infer connectivity + shared = set([s.id for n in network_list(self.request, shared=True) + for s in n.subnets]) + return reachable_subnets | shared def list_targets(self): tenant_id = self.request.user.tenant_id @@ -516,6 +533,60 @@ return c +def list_resources_with_long_filters(list_method, + filter_attr, filter_values, **params): + """List neutron resources with handling RequestURITooLong exception. + + If filter parameters are long, list resources API request leads to + 414 error (URL is too long). For such case, this method split + list parameters specified by a list_field argument into chunks + and call the specified list_method repeatedly. + + :param list_method: Method used to retrieve resource list. + :param filter_attr: attribute name to be filtered. The value corresponding + to this attribute is specified by "filter_values". + If you want to specify more attributes for a filter condition, + pass them as keyword arguments like "attr2=values2". + :param filter_values: values of "filter_attr" to be filtered. + If filter_values are too long and the total URI length exceed the + maximum length supported by the neutron server, filter_values will + be split into sub lists if filter_values is a list. + :param params: parameters to pass a specified listing API call + without any changes. You can specify more filter conditions + in addition to a pair of filter_attr and filter_values. + """ + try: + params[filter_attr] = filter_values + return list_method(**params) + except neutron_exc.RequestURITooLong as uri_len_exc: + # The URI is too long because of too many filter values. + # Use the excess attribute of the exception to know how many + # filter values can be inserted into a single request. + + # We consider only the filter condition from (filter_attr, + # filter_values) and do not consider other filter conditions + # which may be specified in **params. + if type(filter_values) != list: + filter_values = [filter_values] + + # Length of each query filter is: + # =& (e.g., id=) + # The length will be key_len + value_maxlen + 2 + all_filter_len = sum(len(filter_attr) + len(val) + 2 + for val in filter_values) + allowed_filter_len = all_filter_len - uri_len_exc.excess + + val_maxlen = max(len(val) for val in filter_values) + filter_maxlen = len(filter_attr) + val_maxlen + 2 + chunk_size = allowed_filter_len / filter_maxlen + + resources = [] + for i in range(0, len(filter_values), chunk_size): + params[filter_attr] = filter_values[i:i + chunk_size] + resources.extend(list_method(**params)) + return resources + + def network_list(request, **params): LOG.debug("network_list(): params=%s", params) networks = neutronclient(request).list_networks(**params).get('networks') @@ -563,6 +634,7 @@ if expand_subnet: network['subnets'] = [subnet_get(request, sid) for sid in network['subnets']] + return Network(network) @@ -661,6 +733,13 @@ return Port(port) +def unescape_port_kwargs(**kwargs): + for key in kwargs: + if '__' in key: + kwargs[':'.join(key.split('__'))] = kwargs.pop(key) + return kwargs + + def port_create(request, network_id, **kwargs): """Create a port on a specified network. @@ -675,6 +754,7 @@ # In the case policy profiles are being used, profile id is needed. if 'policy_profile_id' in kwargs: kwargs['n1kv:profile_id'] = kwargs.pop('policy_profile_id') + kwargs = unescape_port_kwargs(**kwargs) body = {'port': {'network_id': network_id}} if 'tenant_id' not in kwargs: kwargs['tenant_id'] = request.user.project_id @@ -690,6 +770,7 @@ def port_update(request, port_id, **kwargs): LOG.debug("port_update(): portid=%s, kwargs=%s" % (port_id, kwargs)) + kwargs = unescape_port_kwargs(**kwargs) body = {'port': kwargs} port = neutronclient(request).update_port(port_id, body=body).get('port') return Port(port) @@ -859,16 +940,19 @@ # Get all (filtered for relevant servers) information from Neutron try: - ports = port_list(request, - device_id=[instance.id for instance in servers]) + ports = list_resources_with_long_filters( + port_list, 'device_id', [instance.id for instance in servers], + request=request) fips = FloatingIpManager(request) if fips.is_supported(): - floating_ips = fips.list(all_tenants=all_tenants, - port_id=[port.id for port in ports]) + floating_ips = list_resources_with_long_filters( + fips.list, 'port_id', [port.id for port in ports], + all_tenants=all_tenants) else: floating_ips = [] - networks = network_list(request, - id=[port.network_id for port in ports]) + networks = list_resources_with_long_filters( + network_list, 'id', set([port.network_id for port in ports]), + request=request) except Exception: error_message = _('Unable to connect to Neutron.') LOG.error(error_message) diff -Nru horizon-2015.1~b2/openstack_dashboard/api/nova.py horizon-2015.1~b3/openstack_dashboard/api/nova.py --- horizon-2015.1~b2/openstack_dashboard/api/nova.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/nova.py 2015-03-19 19:07:23.000000000 +0000 @@ -75,6 +75,14 @@ _attrs = ['url', 'type'] +class SerialConsole(base.APIDictWrapper): + """Wrapper for the "console" dictionary. + + Returned by the novaclient.servers.get_serial_console method. + """ + _attrs = ['url', 'type'] + + class Server(base.APIResourceWrapper): """Simple wrapper around novaclient.server.Server. @@ -206,6 +214,9 @@ for rule in self._apiresource.rules] return [SecurityGroupRule(rule) for rule in rule_objs] + def to_dict(self): + return self._apiresource.to_dict() + class SecurityGroupRule(base.APIResourceWrapper): """Wrapper for individual rules in a SecurityGroup.""" @@ -455,6 +466,11 @@ instance_id, console_type)['console']) +def server_serial_console(request, instance_id, console_type='serial'): + return SerialConsole(novaclient(request).servers.get_serial_console( + instance_id, console_type)['console']) + + def flavor_create(request, name, memory, vcpu, disk, flavorid='auto', ephemeral=0, swap=0, metadata=None, is_public=True): flavor = novaclient(request).flavors.create(name, memory, vcpu, disk, @@ -470,14 +486,21 @@ novaclient(request).flavors.delete(flavor_id) -def flavor_get(request, flavor_id): - return novaclient(request).flavors.get(flavor_id) +def flavor_get(request, flavor_id, get_extras=False): + flavor = novaclient(request).flavors.get(flavor_id) + if get_extras: + flavor.extras = flavor_get_extras(request, flavor.id, True, flavor) + return flavor @memoized -def flavor_list(request, is_public=True): +def flavor_list(request, is_public=True, get_extras=False): """Get the list of available instance sizes (flavors).""" - return novaclient(request).flavors.list(is_public=is_public) + flavors = novaclient(request).flavors.list(is_public=is_public) + if get_extras: + for flavor in flavors: + flavor.extras = flavor_get_extras(request, flavor.id, True, flavor) + return flavors @memoized @@ -498,9 +521,10 @@ flavor=flavor, tenant=tenant) -def flavor_get_extras(request, flavor_id, raw=False): +def flavor_get_extras(request, flavor_id, raw=False, flavor=None): """Get flavor extra specs.""" - flavor = novaclient(request).flavors.get(flavor_id) + if flavor is None: + flavor = novaclient(request).flavors.get(flavor_id) extras = flavor.get_keys() if raw: return extras @@ -777,6 +801,42 @@ raise nova_exceptions.ClientException(err_code, msg) return True + + +def migrate_host(request, host, live_migrate=False, disk_over_commit=False, + block_migration=False): + hypervisors = novaclient(request).hypervisors.search(host, True) + response = [] + err_code = None + for hyper in hypervisors: + for server in getattr(hyper, "servers", []): + try: + if live_migrate: + instance = server_get(request, server['uuid']) + + # Checking that instance can be live-migrated + if instance.status in ["ACTIVE", "PAUSED"]: + novaclient(request).servers.live_migrate( + server['uuid'], + None, + block_migration, + disk_over_commit + ) + else: + novaclient(request).servers.migrate(server['uuid']) + else: + novaclient(request).servers.migrate(server['uuid']) + except nova_exceptions.ClientException as err: + err_code = err.code + msg = _("Name: %(name)s ID: %(uuid)s") + msg = msg % {'name': server['name'], 'uuid': server['uuid']} + response.append(msg) + + if err_code: + msg = _('Failed to migrate instances: %s') % ', '.join(response) + raise nova_exceptions.ClientException(err_code, msg) + + return True def tenant_absolute_limits(request, reserved=False): diff -Nru horizon-2015.1~b2/openstack_dashboard/api/rest/cinder.py horizon-2015.1~b3/openstack_dashboard/api/rest/cinder.py --- horizon-2015.1~b2/openstack_dashboard/api/rest/cinder.py 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/rest/cinder.py 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,75 @@ +# 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. +"""API over the cinder service. +""" + +from django.views import generic + +from openstack_dashboard import api +from openstack_dashboard.api.rest import utils as rest_utils + +from openstack_dashboard.api.rest import urls + + +@urls.register +class Volumes(generic.View): + """API for cinder volumes. + """ + url_regex = r'cinder/volumes/$' + + @rest_utils.ajax() + def get(self, request): + """Get a detailed list of volumes associated with the current user's + project. + + If invoked as an admin, you may set the GET parameter "all_projects" + to 'true'. + + The following get parameters may be passed in the GET + + :param search_opts includes options such as name, status, bootable + + The listing result is an object with property "items". + """ + # TODO(clu_): when v2 pagination stuff in Cinder API merges + # (https://review.openstack.org/#/c/118450), handle here accordingly + + if request.GET.get('all_projects') == 'true': + result = api.cinder.volume_list(request, {'all_tenants': 1}) + else: + result = api.cinder.volume_list( + request, + search_opts=rest_utils.parse_filters_kwargs(request)[0] + ) + return {'items': [u.to_dict() for u in result]} + + +@urls.register +class VolumeSnapshots(generic.View): + """API for cinder volume snapshots. + """ + url_regex = r'cinder/volumesnapshots/$' + + @rest_utils.ajax() + def get(self, request): + """Get a detailed list of volume snapshots associated with the current + user's project. + + The listing result is an object with property "items". + """ + result = api.cinder.volume_snapshot_list( + request, + search_opts=rest_utils.parse_filters_kwargs(request)[0] + ) + return {'items': [u.to_dict() for u in result]} diff -Nru horizon-2015.1~b2/openstack_dashboard/api/rest/config.py horizon-2015.1~b3/openstack_dashboard/api/rest/config.py --- horizon-2015.1~b2/openstack_dashboard/api/rest/config.py 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/rest/config.py 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,72 @@ +# 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. + +from django.views import generic + +from horizon import conf + +from openstack_dashboard.api.rest import urls +from openstack_dashboard.api.rest import utils as rest_utils + + +# properties we know are admin config +admin_configs = ['ajax_queue_limit', 'ajax_poll_interval', + 'user_home', 'help_url', + 'password_autocomplete', 'disable_password_reveal'] + +# properties we know are user config +# this is a white list of keys under HORIZON_CONFIG in settings.pys +# that we want to pass onto client +user_configs = ['auto_fade_alerts', 'modal_backdrop'] + + +@urls.register +class DefaultUserConfigs(generic.View): + """API for retrieving user configurations. + + This API returns read-only-default configuration values. + This configuration object is ideally fetched once per application life + or when a user needs to restore the default values. + Examples of user config: modal_backdrop, disable_password_reveal + """ + url_regex = r'config/user/$' + + @rest_utils.ajax() + def get(self, request): + """Get default user configurations + """ + config = {} + for key in user_configs: + config[key] = conf.HORIZON_CONFIG.get(key, None) + return config + + +@urls.register +class AdminConfigs(generic.View): + """API for retrieving admin configurations. + + This API returns read-only admin configuration values. + This configuration object can be fetched as needed. + Examples of admin config: help_url, user_home + """ + url_regex = r'config/admin/$' + + @rest_utils.ajax() + def get(self, request): + """Get read-only admin configurations + """ + config = {} + for key in admin_configs: + config[key] = conf.HORIZON_CONFIG.get(key, None) + return config diff -Nru horizon-2015.1~b2/openstack_dashboard/api/rest/glance.py horizon-2015.1~b3/openstack_dashboard/api/rest/glance.py --- horizon-2015.1~b2/openstack_dashboard/api/rest/glance.py 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/rest/glance.py 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,160 @@ + +# 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. +"""API for the glance service. +""" + +from django.views import generic + +from openstack_dashboard import api +from openstack_dashboard.api.rest import utils as rest_utils +from openstack_dashboard.api.rest import urls + + +CLIENT_KEYWORDS = {'marker', 'sort_dir', 'sort_key', 'paginate'} + + +@urls.register +class Image(generic.View): + """API for retrieving a single image + """ + url_regex = r'glance/images/(?P.+|default)$' + + @rest_utils.ajax() + def get(self, request, image_id): + """Get a specific image + + http://localhost/api/glance/images/cc758c90-3d98-4ea1-af44-aab405c9c915 + """ + return api.glance.image_get(request, image_id).to_dict() + + +@urls.register +class Images(generic.View): + """API for Glance images. + """ + url_regex = r'glance/images/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of images. + + The listing result is an object with property "items". Each item is + an image. + + Example GET: + http://localhost/api/glance/images?sort_dir=desc&sort_key=name&name=cirros-0.3.2-x86_64-uec #flake8: noqa + + The following get parameters may be passed in the GET + request: + + :param paginate: If true will perform pagination based on settings. + :param marker: Specifies the namespace of the last-seen image. + The typical pattern of limit and marker is to make an + initial limited request and then to use the last + namespace from the response as the marker parameter + in a subsequent limited request. With paginate, limit + is automatically set. + :param sort_dir: The sort direction ('asc' or 'desc'). + :param sort_key: The field to sort on (for example, 'created_at'). + Default is created_at. + + Any additional request parameters will be passed through the API as + filters. There are v1/v2 complications which are being addressed as a + separate work stream: https://review.openstack.org/#/c/150084/ + """ + + filters, kwargs = rest_utils.parse_filters_kwargs(request, + CLIENT_KEYWORDS) + + images, has_more_data, has_prev_data = api.glance.image_list_detailed( + request, filters=filters, **kwargs) + + return { + 'items': [i.to_dict() for i in images], + 'has_more_data': has_more_data, + 'has_prev_data': has_prev_data, + } + + +@urls.register +class MetadefsNamespace(generic.View): + """API for Glance Metadata Definitions. + + http://docs.openstack.org/developer/glance/metadefs-concepts.html + """ + url_regex = r'glance/metadefs/namespaces/(?P.+|default)$' + + @rest_utils.ajax() + def get(self, request, namespace): + """Get a specific metadata definition namespaces. + + Returns the namespace. GET params are passed through. + + Example GET: + http://localhost/api/glance/metadefs/namespaces/OS::Compute::Watchdog + """ + return api.glance.metadefs_namespace_get(request, namespace) + + +@urls.register +class MetadefsNamespaces(generic.View): + """API for Single Glance Metadata Definitions. + + http://docs.openstack.org/developer/glance/metadefs-concepts.html + """ + url_regex = r'glance/metadefs/namespaces/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of metadata definition namespaces. + + The listing result is an object with property "items". Each item is + a namespace. + + Example GET: + http://localhost/api/glance/metadefs/namespaces?resource_types=OS::Nova::Flavor&sort_dir=desc&marker=OS::Compute::Watchdog&paginate=False&sort_key=namespace #flake8: noqa + + The following get parameters may be passed in the GET + request: + + :param paginate: If true will perform pagination based on settings. + :param marker: Specifies the namespace of the last-seen namespace. + The typical pattern of limit and marker is to make an + initial limited request and then to use the last + namespace from the response as the marker parameter + in a subsequent limited request. With paginate, limit + is automatically set. + :param sort_dir: The sort direction ('asc' or 'desc'). + :param sort_key: The field to sort on (for example, 'created_at'). + Default is namespace. The way base namespaces are loaded into + glance typically at first deployment is done in a single + transaction giving them a potentially unpredictable sort result + when using create_at. + + Any additional request parameters will be passed through the API as + filters. + """ + + filters, kwargs = rest_utils.parse_filters_kwargs(request, + CLIENT_KEYWORDS) + + namespaces, has_more, has_prev = api.glance.metadefs_namespace_list( + request, filters=filters, **kwargs) + + return { + 'items': [n.to_dict() for n in namespaces], + 'has_more_data': has_more, + 'has_prev_data': has_prev, + } diff -Nru horizon-2015.1~b2/openstack_dashboard/api/rest/__init__.py horizon-2015.1~b3/openstack_dashboard/api/rest/__init__.py --- horizon-2015.1~b2/openstack_dashboard/api/rest/__init__.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/rest/__init__.py 2015-03-19 19:07:23.000000000 +0000 @@ -22,4 +22,11 @@ """ # import REST API modules here +import cinder #flake8: noqa +import config #flake8: noqa +import glance #flake8: noqa import keystone #flake8: noqa +import network #flake8: noqa +import neutron #flake8: noqa +import nova #flake8: noqa +import policy #flake8: noqa diff -Nru horizon-2015.1~b2/openstack_dashboard/api/rest/keystone.py horizon-2015.1~b3/openstack_dashboard/api/rest/keystone.py --- horizon-2015.1~b2/openstack_dashboard/api/rest/keystone.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/rest/keystone.py 2015-03-19 19:07:23.000000000 +0000 @@ -28,23 +28,31 @@ """API for keystone users. """ url_regex = r'keystone/users/$' + client_keywords = {'project_id', 'domain_id', 'group_id'} @rest_utils.ajax() def get(self, request): """Get a list of users. By default, a listing of all users for the current domain are - returned. You may specify GET parameters for project_id, group_id and - domain_id to change that listing's context. + returned. You may specify GET parameters for project_id, domain_id and + group_id to change that listing's context. The listing result is an object with property "items". """ domain_context = request.session.get('domain_context') + + filters = rest_utils.parse_filters_kwargs(request, + self.client_keywords)[0] + if len(filters) == 0: + filters = None + result = api.keystone.user_list( request, project=request.GET.get('project_id'), domain=request.GET.get('domain_id', domain_context), - group=request.GET.get('group_id') + group=request.GET.get('group_id'), + filters=filters ) return {'items': [u.to_dict() for u in result]} @@ -358,6 +366,8 @@ interchangeably. """ url_regex = r'keystone/projects/$' + client_keywords = {'paginate', 'marker', 'domain_id', + 'user_id', 'admin'} @rest_utils.ajax() def get(self, request): @@ -366,7 +376,7 @@ By default a listing of all projects for the current domain are returned. - You may specify GET parameters for project_id (string), user_id + You may specify GET parameters for domain_id (string), user_id (string) and admin (boolean) to change that listing's context. Additionally, paginate (boolean) and marker may be used to get paginated listings. @@ -378,13 +388,20 @@ has_more Boolean indicating there are more results when pagination is used. """ + + filters = rest_utils.parse_filters_kwargs(request, + self.client_keywords)[0] + if len(filters) == 0: + filters = None + result, has_more = api.keystone.tenant_list( request, paginate=request.GET.get('paginate', False), marker=request.GET.get('marker'), domain=request.GET.get('domain_id'), user=request.GET.get('user_id'), - admin=request.GET.get('admin', True) + admin=request.GET.get('admin', True), + filters=filters ) # return (list of results, has_more_data) return dict(has_more=has_more, items=[d.to_dict() for d in result]) @@ -436,7 +453,7 @@ underlying keystone API the terms "project" and "tenant" are used interchangeably. """ - url_regex = r'keystone/projects/$' + url_regex = r'keystone/projects/(?P[0-9a-f]+)$' @rest_utils.ajax() def get(self, request, id): @@ -487,3 +504,15 @@ user_id, role_id ) + + +@urls.register +class ServiceCatalog(generic.View): + url_regex = r'keystone/svc-catalog/$' + + @rest_utils.ajax() + def get(self, request): + """Return the Keystone service catalog associated with the current + user. + """ + return request.user.service_catalog diff -Nru horizon-2015.1~b2/openstack_dashboard/api/rest/network.py horizon-2015.1~b3/openstack_dashboard/api/rest/network.py --- horizon-2015.1~b2/openstack_dashboard/api/rest/network.py 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/rest/network.py 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,46 @@ + +# 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. +"""API for the network abstraction APIs. +""" + +from django.views import generic + +from openstack_dashboard import api +from openstack_dashboard.api.rest import urls +from openstack_dashboard.api.rest import utils as rest_utils + + +@urls.register +class SecurityGroups(generic.View): + """API for Network Abstraction + + Handles differences between Nova and Neutron. + """ + url_regex = r'network/securitygroups/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of security groups. + + The listing result is an object with property "items". Each item is + an image. + + Example GET: + http://localhost/api/network/securitygroups + """ + + security_groups = api.network.security_group_list(request) + + return {'items': [sg.to_dict() for sg in security_groups]} diff -Nru horizon-2015.1~b2/openstack_dashboard/api/rest/neutron.py horizon-2015.1~b3/openstack_dashboard/api/rest/neutron.py --- horizon-2015.1~b2/openstack_dashboard/api/rest/neutron.py 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/rest/neutron.py 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,136 @@ +# +# (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. +"""API over the neutron service. +""" + +from django.views import generic + +from openstack_dashboard import api +from openstack_dashboard.api.rest import utils as rest_utils + +from openstack_dashboard.api.rest import urls + + +@urls.register +class Networks(generic.View): + """API for Neutron Networks + http://developer.openstack.org/api-ref-networking-v2.html + """ + url_regex = r'neutron/networks/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of networks for a project + + The listing result is an object with property "items". Each item is + a network. + """ + tenant_id = request.user.tenant_id + result = api.neutron.network_list_for_tenant(request, tenant_id) + return{'items': [n.to_dict() for n in result]} + + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a network + :param admin_state_up (optional): The administrative state of the + network, which is up (true) or down (false). + :param name (optional): The network name. A request body is optional: + If you include it, it can specify this optional attribute. + :param net_profile_id (optional): network profile id + :param shared (optional): Indicates whether this network is shared + across all tenants. By default, only administrative users can + change this value. + :param tenant_id (optional): Admin-only. The UUID of the tenant that + will own the network. This tenant can be different from the + tenant that makes the create network request. However, only + administrative users can specify a tenant ID other than their + own. You cannot change this value through authorization + policies. + + :return: JSON representation of a Network + """ + if not api.neutron.is_port_profiles_supported(): + request.DATA.pop("net_profile_id", None) + new_network = api.neutron.network_create(request, **request.DATA) + return rest_utils.CreatedResponse( + '/api/neutron/networks/%s' % new_network.id, + new_network.to_dict() + ) + + +@urls.register +class Subnets(generic.View): + """API for Neutron SubNets + http://developer.openstack.org/api-ref-networking-v2.html#subnets + """ + url_regex = r'neutron/subnets/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of subnets for a project + + The listing result is an object with property "items". Each item is + a subnet. + + """ + result = api.neutron.subnet_list(request, **request.GET) + return{'items': [n.to_dict() for n in result]} + + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a Subnet for a given Network + + :param name (optional): The subnet name. + :param network_id: The ID of the attached network. + :param tenant_id (optional): The ID of the tenant who owns the network. + Only administrative users can specify a tenant ID other than + their own. + :param allocation_pools (optional): The start and end addresses for the + allocation pools. + :param gateway_ip (optional): The gateway IP address. + :param ip_version: The IP version, which is 4 or 6. + :param cidr: The CIDR. + :param id (optional): The ID of the subnet. + :param enable_dhcp (optional): Set to true if DHCP is enabled and false + if DHCP is disabled. + + :return: JSON representation of a Subnet + + """ + new_subnet = api.neutron.subnet_create(request, **request.DATA) + return rest_utils.CreatedResponse( + '/api/neutron/subnets/%s' % new_subnet.id, + new_subnet.to_dict() + ) + + +@urls.register +class Ports(generic.View): + """API for Neutron Ports + http://developer.openstack.org/api-ref-networking-v2.html#ports + """ + url_regex = r'neutron/ports/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of ports for a network + + The listing result is an object with property "items". Each item is + a subnet. + """ + # see + # https://github.com/openstack/neutron/blob/master/neutron/api/v2/attributes.py + result = api.neutron.port_list(request, **request.GET) + return{'items': [n.to_dict() for n in result]} diff -Nru horizon-2015.1~b2/openstack_dashboard/api/rest/nova.py horizon-2015.1~b3/openstack_dashboard/api/rest/nova.py --- horizon-2015.1~b2/openstack_dashboard/api/rest/nova.py 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/rest/nova.py 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,286 @@ + +# 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. +"""API over the nova service. +""" + +from django.utils import http as utils_http +from django.views import generic + +from openstack_dashboard import api +from openstack_dashboard.api.rest import urls +from openstack_dashboard.api.rest import utils as rest_utils + + +@urls.register +class Keypairs(generic.View): + """API for nova keypairs. + """ + url_regex = r'nova/keypairs/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of keypairs associated with the current logged-in + account. + + The listing result is an object with property "items". + """ + result = api.nova.keypair_list(request) + return {'items': [u.to_dict() for u in result]} + + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a keypair. + + Create a keypair using the parameters supplied in the POST + application/json object. The parameters are: + + :param name: the name to give the keypair + :param public_key: (optional) a key to import + + This returns the new keypair object on success. + """ + if 'public_key' in request.DATA: + new = api.nova.keypair_import(request, request.DATA['name'], + request.DATA['public_key']) + else: + new = api.nova.keypair_create(request, request.DATA['name']) + return rest_utils.CreatedResponse( + '/api/nova/keypairs/%s' % utils_http.urlquote(new.name), + new.to_dict() + ) + + +@urls.register +class AvailabilityZones(generic.View): + """API for nova availability zones. + """ + url_regex = r'nova/availzones/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of availability zones. + + The following get parameters may be passed in the GET + request: + + :param detailed: If this equals "true" then the result will + include more detail. + + The listing result is an object with property "items". + """ + detailed = request.GET.get('detailed') == 'true' + result = api.nova.availability_zone_list(request, detailed) + return {'items': [u.to_dict() for u in result]} + + +@urls.register +class Limits(generic.View): + """API for nova limits. + """ + url_regex = r'nova/limits/$' + + @rest_utils.ajax() + def get(self, request): + """Get an object describing the current project limits. + + Note: the Horizon API doesn't support any other project (tenant) but + the underlying client does... + + The following get parameters may be passed in the GET + request: + + :param reserved: This may be set to "true" but it's not + clear what the result of that is. + + The result is an object with limits as properties. + """ + reserved = request.GET.get('reserved') == 'true' + result = api.nova.tenant_absolute_limits(request, reserved) + return result + + +@urls.register +class Servers(generic.View): + """API over all servers. + """ + url_regex = r'nova/servers/$' + + _optional_create = [ + 'block_device_mapping', 'block_device_mapping_v2', 'nics', 'meta', + 'availability_zone', 'instance_count', 'admin_pass', 'disk_config', + 'config_drive' + ] + + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a server. + + Create a server using the parameters supplied in the POST + application/json object. The required parameters as specified by + the underlying novaclient are: + + :param name: The new server name. + :param source_id: The ID of the image to use. + :param flavor_id: The ID of the flavor to use. + :param key_name: (optional extension) name of previously created + keypair to inject into the instance. + :param user_data: user data to pass to be exposed by the metadata + server this can be a file type object as well or a + string. + :param security_groups: An array of one or more objects with a "name" + attribute. + + Other parameters are accepted as per the underlying novaclient: + "block_device_mapping", "block_device_mapping_v2", "nics", "meta", + "availability_zone", "instance_count", "admin_pass", "disk_config", + "config_drive" + + This returns the new server object on success. + """ + try: + args = ( + request, + request.DATA['name'], + request.DATA['source_id'], + request.DATA['flavor_id'], + request.DATA['key_name'], + request.DATA['user_data'], + request.DATA['security_groups'], + ) + except KeyError as e: + raise rest_utils.AjaxError(400, 'missing required parameter ' + "'%s'" % e.args[0]) + kw = {} + for name in self._optional_create: + if name in request.DATA: + kw[name] = request.DATA[name] + + new = api.nova.server_create(*args, **kw) + return rest_utils.CreatedResponse( + '/api/nova/servers/%s' % utils_http.urlquote(new.id), + new.to_dict() + ) + + +@urls.register +class Server(generic.View): + """API for retrieving a single server + """ + url_regex = r'nova/servers/(?P.+|default)$' + + @rest_utils.ajax() + def get(self, request, server_id): + """Get a specific server + + http://localhost/api/nova/servers/1 + """ + return api.nova.server_get(request, server_id).to_dict() + + +@urls.register +class Extensions(generic.View): + """API for nova extensions. + """ + url_regex = r'nova/extensions/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of extensions. + + The listing result is an object with property "items". Each item is + an image. + + Example GET: + http://localhost/api/nova/extensions + """ + result = api.nova.list_extensions(request) + return {'items': [e.to_dict() for e in result]} + + +@urls.register +class Flavors(generic.View): + """API for nova flavors. + """ + url_regex = r'nova/flavors/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of flavors. + + The listing result is an object with property "items". Each item is + an flavor. By default this will return the flavors for the user's + current project. If the user is admin, public flavors will also be + returned. + + :param is_public: For a regular user, set to True to see all public + flavors. For an admin user, set to False to not see public flavors. + :param get_extras: Also retrieve the extra specs. + + Example GET: + http://localhost/api/nova/flavors?is_public=true + """ + is_public = request.GET.get('is_public') + is_public = (is_public and is_public.lower() == 'true') + get_extras = request.GET.get('get_extras') + get_extras = bool(get_extras and get_extras.lower() == 'true') + flavors = api.nova.flavor_list(request, is_public=is_public, + get_extras=get_extras) + result = {'items': []} + for flavor in flavors: + d = flavor.to_dict() + if get_extras: + d['extras'] = flavor.extras + result['items'].append(d) + return result + + +@urls.register +class Flavor(generic.View): + """API for retrieving a single flavor + """ + url_regex = r'nova/flavors/(?P.+)/$' + + @rest_utils.ajax() + def get(self, request, flavor_id): + """Get a specific flavor + + :param get_extras: Also retrieve the extra specs. + + Example GET: + http://localhost/api/nova/flavors/1 + """ + get_extras = request.GET.get('get_extras') + get_extras = bool(get_extras and get_extras.lower() == 'true') + flavor = api.nova.flavor_get(request, flavor_id, get_extras=get_extras) + result = flavor.to_dict() + if get_extras: + result['extras'] = flavor.extras + return result + + +@urls.register +class FlavorExtraSpecs(generic.View): + """API for managing flavor extra specs + """ + url_regex = r'nova/flavors/(?P.+)/extra-specs$' + + @rest_utils.ajax() + def get(self, request, flavor_id): + """Get a specific flavor's extra specs + + Example GET: + http://localhost/api/nova/flavors/1/extra-specs + """ + return api.nova.flavor_get_extras(request, flavor_id, raw=True) diff -Nru horizon-2015.1~b2/openstack_dashboard/api/rest/policy.py horizon-2015.1~b3/openstack_dashboard/api/rest/policy.py --- horizon-2015.1~b2/openstack_dashboard/api/rest/policy.py 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/rest/policy.py 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,51 @@ +# +# 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.views import generic + +from openstack_dashboard import policy + +from openstack_dashboard.api.rest import urls +from openstack_dashboard.api.rest import utils as rest_utils + + +@urls.register +class Policy(generic.View): + '''API for interacting with the policy engine.''' + + url_regex = r'policy/$' + + @rest_utils.ajax(data_required=True) + def post(self, request): + '''Check policy rules. + + Check the group of policy rules supplied in the POST + application/json object. The policy target, if specified will also be + passed in to the policy check method as well. + + The action returns an object with one key: "allowed" and the value + is the result of the policy check, True or False. + ''' + + rules = [] + try: + rules_in = request.DATA['rules'] + rules = tuple([tuple(rule) for rule in rules_in]) + except Exception: + raise rest_utils.AjaxError(400, 'unexpected parameter format') + + policy_target = request.DATA.get('target') or {} + + result = policy.check(rules, request, policy_target) + + return {"allowed": result} diff -Nru horizon-2015.1~b2/openstack_dashboard/api/rest/utils.py horizon-2015.1~b3/openstack_dashboard/api/rest/utils.py --- horizon-2015.1~b2/openstack_dashboard/api/rest/utils.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/rest/utils.py 2015-03-19 19:07:23.000000000 +0000 @@ -121,8 +121,11 @@ # exception was raised with a specific HTTP status if hasattr(e, 'http_status'): http_status = e.http_status - else: + elif hasattr(e, 'code'): http_status = e.code + else: + log.exception('HTTP exception with no status/code') + return JSONResponse(str(e), 500) return JSONResponse(str(e), http_status) except Exception as e: log.exception('error invoking apiclient') @@ -130,3 +133,20 @@ return _wrapped return decorator + + +def parse_filters_kwargs(request, client_keywords={}): + """Extract REST filter parameters from the request GET args. + + Client processes some keywords separately from filters and takes + them as separate inputs. This will ignore those keys to avoid + potential conflicts. + """ + filters = {} + kwargs = {} + for param in request.GET: + if param in client_keywords: + kwargs[param] = request.GET[param] + else: + filters[param] = request.GET[param] + return filters, kwargs diff -Nru horizon-2015.1~b2/openstack_dashboard/api/sahara.py horizon-2015.1~b3/openstack_dashboard/api/sahara.py --- horizon-2015.1~b2/openstack_dashboard/api/sahara.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/sahara.py 2015-03-19 19:07:29.000000000 +0000 @@ -15,6 +15,7 @@ from django.conf import settings +from horizon import exceptions from horizon.utils.memoized import memoized # noqa from openstack_dashboard.api import base @@ -25,7 +26,9 @@ # "type" of Sahara service registered in keystone -SAHARA_SERVICE = 'data_processing' +SAHARA_SERVICE = 'data-processing' +# Sahara service_type registered in Juno +SAHARA_SERVICE_FALLBACK = 'data_processing' SAHARA_AUTO_IP_ALLOCATION_ENABLED = getattr( settings, @@ -42,15 +45,23 @@ @memoized def client(request): + try: + service_type = SAHARA_SERVICE + sahara_url = base.url_for(request, service_type) + except exceptions.ServiceCatalogException: + # if no endpoint found, fallback to the old service_type + service_type = SAHARA_SERVICE_FALLBACK + sahara_url = base.url_for(request, service_type) + return api_client.Client(VERSIONS.get_active_version()["version"], - sahara_url=base.url_for(request, SAHARA_SERVICE), - service_type=SAHARA_SERVICE, + sahara_url=sahara_url, + service_type=service_type, project_id=request.user.project_id, input_auth_token=request.user.token.id) -def image_list(request): - return client(request).images.list() +def image_list(request, search_opts=None): + return client(request).images.list(search_opts) def image_get(request, image_id): @@ -69,8 +80,8 @@ client(request).images.update_tags(image_id, image_tags) -def plugin_list(request): - return client(request).plugins.list() +def plugin_list(request, search_opts=None): + return client(request).plugins.list(search_opts) def plugin_get(request, plugin_name): @@ -96,22 +107,27 @@ node_processes=None, node_configs=None, floating_ip_pool=None, security_groups=None, auto_security_group=False, - availability_zone=False): - return client(request).node_group_templates.create(name, plugin_name, - hadoop_version, - flavor_id, description, - volumes_per_node, - volumes_size, - node_processes, - node_configs, - floating_ip_pool, - security_groups, - auto_security_group, - availability_zone) + availability_zone=False, + volumes_availability_zone=False): + return client(request).node_group_templates.create( + name, + plugin_name, + hadoop_version, + flavor_id, + description, + volumes_per_node, + volumes_size, + node_processes, + node_configs, + floating_ip_pool, + security_groups, + auto_security_group, + availability_zone, + volumes_availability_zone) -def nodegroup_template_list(request): - return client(request).node_group_templates.list() +def nodegroup_template_list(request, search_opts=None): + return client(request).node_group_templates.list(search_opts) def nodegroup_template_get(request, ngt_id): @@ -132,20 +148,24 @@ volumes_size=None, node_processes=None, node_configs=None, floating_ip_pool=None, security_groups=None, auto_security_group=False, - availability_zone=False): - return client(request).node_group_templates.update(ngt_id, name, - plugin_name, - hadoop_version, - flavor_id, - description, - volumes_per_node, - volumes_size, - node_processes, - node_configs, - floating_ip_pool, - security_groups, - auto_security_group, - availability_zone) + availability_zone=False, + volumes_availability_zone=False): + return client(request).node_group_templates.update( + ngt_id, + name, + plugin_name, + hadoop_version, + flavor_id, + description, + volumes_per_node, + volumes_size, + node_processes, + node_configs, + floating_ip_pool, + security_groups, + auto_security_group, + availability_zone, + volumes_availability_zone) def cluster_template_create(request, name, plugin_name, hadoop_version, @@ -161,8 +181,8 @@ net_id) -def cluster_template_list(request): - return client(request).cluster_templates.list() +def cluster_template_list(request, search_opts=None): + return client(request).cluster_templates.list(search_opts) def cluster_template_get(request, ct_id): @@ -205,8 +225,8 @@ return client(request).clusters.scale(cluster_id, scale_object) -def cluster_list(request): - return client(request).clusters.list() +def cluster_list(request, search_opts=None): + return client(request).clusters.list(search_opts) def cluster_get(request, cluster_id): @@ -224,8 +244,8 @@ credential_pass) -def data_source_list(request): - return client(request).data_sources.list() +def data_source_list(request, search_opts=None): + return client(request).data_sources.list(search_opts) def data_source_get(request, ds_id): @@ -240,8 +260,8 @@ return client(request).job_binaries.create(name, url, description, extra) -def job_binary_list(request): - return client(request).job_binaries.list() +def job_binary_list(request, search_opts=None): + return client(request).job_binaries.list(search_opts) def job_binary_get(request, jb_id): @@ -260,8 +280,8 @@ return client(request).job_binary_internals.create(name, data) -def job_binary_internal_list(request): - return client(request).job_binary_internals.list() +def job_binary_internal_list(request, search_opts=None): + return client(request).job_binary_internals.list(search_opts) def job_binary_internal_get(request, jbi_id): @@ -276,8 +296,8 @@ return client(request).jobs.create(name, j_type, mains, libs, description) -def job_list(request): - return client(request).jobs.list() +def job_list(request, search_opts=None): + return client(request).jobs.list(search_opts) def job_get(request, job_id): @@ -299,8 +319,8 @@ configs) -def job_execution_list(request): - jex_list = client(request).job_executions.list() +def job_execution_list(request, search_opts=None): + jex_list = client(request).job_executions.list(search_opts) job_dict = dict((j.id, j) for j in job_list(request)) cluster_dict = dict((c.id, c) for c in cluster_list(request)) for jex in jex_list: diff -Nru horizon-2015.1~b2/openstack_dashboard/api/trove.py horizon-2015.1~b3/openstack_dashboard/api/trove.py --- horizon-2015.1~b2/openstack_dashboard/api/trove.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/api/trove.py 2015-03-19 19:07:23.000000000 +0000 @@ -57,7 +57,8 @@ def instance_create(request, name, volume, flavor, databases=None, users=None, restore_point=None, nics=None, - datastore=None, datastore_version=None): + datastore=None, datastore_version=None, + replica_of=None): # TODO(dklyle): adding conditional to support trove without volume # support for now until API supports checking for volume support if volume > 0: @@ -73,7 +74,8 @@ restorePoint=restore_point, nics=nics, datastore=datastore, - datastore_version=datastore_version) + datastore_version=datastore_version, + replica_of=replica_of) def instance_resize_volume(request, instance_id, size): @@ -93,6 +95,11 @@ return troveclient(request).instances.restart(instance_id) +def instance_detach_replica(request, instance_id): + return troveclient(request).instances.edit(instance_id, + detach_replica_source=True) + + def database_list(request, instance_id): return troveclient(request).databases.list(instance_id) diff -Nru horizon-2015.1~b2/openstack_dashboard/conf/ceilometer_policy.json horizon-2015.1~b3/openstack_dashboard/conf/ceilometer_policy.json --- horizon-2015.1~b2/openstack_dashboard/conf/ceilometer_policy.json 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/conf/ceilometer_policy.json 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,6 @@ +{ + "context_is_admin": "role:admin", + "context_is_project": "project_id:%(target.project_id)s", + "context_is_owner": "user_id:%(target.user_id)s", + "segregation": "rule:context_is_admin" +} diff -Nru horizon-2015.1~b2/openstack_dashboard/conf/heat_policy.json horizon-2015.1~b3/openstack_dashboard/conf/heat_policy.json --- horizon-2015.1~b2/openstack_dashboard/conf/heat_policy.json 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/conf/heat_policy.json 2015-03-19 19:07:23.000000000 +0000 @@ -4,6 +4,7 @@ "cloudformation:ListStacks": "rule:deny_stack_user", "cloudformation:CreateStack": "rule:deny_stack_user", + "cloudformation:PreviewStack": "rule:deny_stack_user", "cloudformation:DescribeStacks": "rule:deny_stack_user", "cloudformation:DeleteStack": "rule:deny_stack_user", "cloudformation:UpdateStack": "rule:deny_stack_user", diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/create.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/create.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create Host Aggregate" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create Host Aggregate") %} -{% endblock page_header %} - {% block main %} {% include 'horizon/common/_workflow.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Host Aggregates" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Host Aggregates") %} -{% endblock page_header %} - {% block main %}
{{ host_aggregates_table.render }} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/manage_hosts.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/manage_hosts.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/manage_hosts.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/manage_hosts.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Manage Hosts Aggregate" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Manage Hosts Aggregate") %} -{% endblock page_header %} - {% block main %} {% include 'horizon/common/_workflow.html' %} -{% endblock %} \ No newline at end of file +{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,11 +2,6 @@ {% load i18n %} {% block title %}{% trans "Edit Host Aggregate" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Edit Host Aggregate") %} -{% endblock page_header %} - - {% block main %} {% include 'admin/aggregates/_update.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update_metadata.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update_metadata.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update_metadata.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update_metadata.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Update Aggregate Metadata" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Aggregate Metadata") %} -{% endblock page_header %} - {% block main %} {% include 'admin/aggregates/_update_metadata.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/tests.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/tests.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/tests.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/tests.py 2015-03-19 19:07:23.000000000 +0000 @@ -180,13 +180,20 @@ @mock.patch('openstack_dashboard.api.nova.extension_supported', mock.Mock(return_value=False)) + @test.create_stubs({api.nova: ('aggregate_details_list', + 'availability_zone_list',), + api.cinder: ('tenant_absolute_limits',)}) def test_panel_not_available(self): + api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)). \ + MultipleTimes().AndReturn(self.cinder_limits['absolute']) + self.mox.ReplayAll() + self.patchers['aggregates'].stop() res = self.client.get(reverse('horizon:admin:overview:index')) self.assertNotIn('Host Aggregates', res.content) @test.create_stubs({api.nova: ('aggregate_details_list', - 'availability_zone_list',), }) + 'availability_zone_list',)}) def test_index(self): api.nova.aggregate_details_list(IsA(http.HttpRequest)) \ .AndReturn(self.aggregates.list()) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/aggregates/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/aggregates/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -40,6 +40,7 @@ table_classes = (project_tables.HostAggregatesTable, project_tables.AvailabilityZonesTable) template_name = constants.AGGREGATES_TEMPLATE_NAME + page_title = _("Host Aggregates") def get_host_aggregates_data(self): request = self.request @@ -68,12 +69,14 @@ class CreateView(workflows.WorkflowView): workflow_class = aggregate_workflows.CreateAggregateWorkflow template_name = constants.AGGREGATES_CREATE_VIEW_TEMPLATE + page_title = _("Create Host Aggregate") class UpdateView(forms.ModalFormView): template_name = constants.AGGREGATES_UPDATE_VIEW_TEMPLATE form_class = aggregate_forms.UpdateAggregateForm success_url = reverse_lazy(constants.AGGREGATES_INDEX_URL) + page_title = _("Edit Host Aggregate") def get_initial(self): aggregate = self.get_object() @@ -102,6 +105,7 @@ template_name = constants.AGGREGATES_UPDATE_METADATA_TEMPLATE form_class = aggregate_forms.UpdateMetadataForm success_url = reverse_lazy(constants.AGGREGATES_INDEX_URL) + page_title = _("Update Aggregate Metadata") def get_initial(self): aggregate = self.get_object() @@ -150,6 +154,7 @@ template_name = constants.AGGREGATES_MANAGE_HOSTS_TEMPLATE workflow_class = aggregate_workflows.ManageAggregateHostsWorkflow success_url = reverse_lazy(constants.AGGREGATES_INDEX_URL) + page_title = _("Manage Hosts Aggregate") def get_initial(self): return {'id': self.kwargs["id"]} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/defaults/templates/defaults/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/defaults/templates/defaults/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/defaults/templates/defaults/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/defaults/templates/defaults/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Defaults" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Defaults")%} -{% endblock page_header %} - {% block main %}
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/defaults/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/defaults/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/defaults/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/defaults/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -26,6 +26,7 @@ class IndexView(tabs.TabbedTableView): tab_group_class = project_tabs.DefaultsTabs template_name = 'admin/defaults/index.html' + page_title = _("Defaults") class UpdateDefaultQuotasView(workflows.WorkflowView): diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/flavors/templates/flavors/create.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/flavors/templates/flavors/create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/flavors/templates/flavors/create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/flavors/templates/flavors/create.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create Flavor" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create Flavor") %} -{% endblock page_header %} - {% block main %} {% include 'horizon/common/_workflow.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/flavors/templates/flavors/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/flavors/templates/flavors/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/flavors/templates/flavors/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/flavors/templates/flavors/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Flavors" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Flavors") %} -{% endblock page_header %} - {% block main %} {{ table.render }} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/flavors/templates/flavors/update.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/flavors/templates/flavors/update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/flavors/templates/flavors/update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/flavors/templates/flavors/update.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Edit Flavor" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Edit Flavor") %} -{% endblock page_header %} - {% block main %} {% include 'horizon/common/_workflow.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/flavors/templates/flavors/update_metadata.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/flavors/templates/flavors/update_metadata.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/flavors/templates/flavors/update_metadata.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/flavors/templates/flavors/update_metadata.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Update Flavor Metadata" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Flavor Metadata") %} -{% endblock page_header %} - {% block main %} {% include 'admin/flavors/_update_metadata.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/flavors/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/flavors/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/flavors/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/flavors/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -44,6 +44,7 @@ class IndexView(tables.DataTableView): table_class = project_tables.FlavorsTable template_name = 'admin/flavors/index.html' + page_title = _("Flavors") def get_data(self): request = self.request @@ -62,11 +63,13 @@ class CreateView(workflows.WorkflowView): workflow_class = flavor_workflows.CreateFlavor template_name = 'admin/flavors/create.html' + page_title = _("Create Flavor") class UpdateView(workflows.WorkflowView): workflow_class = flavor_workflows.UpdateFlavor template_name = 'admin/flavors/update.html' + page_title = _("Edit Flavor") def get_initial(self): flavor_id = self.kwargs['id'] @@ -91,6 +94,7 @@ template_name = "admin/flavors/update_metadata.html" form_class = project_forms.UpdateMetadataForm success_url = reverse_lazy('horizon:admin:flavors:index') + page_title = _("Update Flavor Metadata") def get_initial(self): extra_specs_dict = self.get_object() diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/compute/forms.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/compute/forms.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/compute/forms.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/compute/forms.py 2015-03-19 19:07:23.000000000 +0000 @@ -92,3 +92,76 @@ data["host"] exceptions.handle(request, message=msg, redirect=redirect) return False + + +class MigrateHostForm(forms.SelfHandlingForm): + current_host = forms.CharField( + label=_("Current Host"), + required=False, + widget=forms.TextInput( + attrs={'readonly': 'readonly'}) + ) + + migrate_type = forms.ChoiceField( + label=_('Running Instance Migration Type'), + choices=[ + ('live_migrate', _('Live Migrate')), + ('cold_migrate', _('Cold Migrate')) + ], + widget=forms.Select( + attrs={ + 'class': 'switchable', + 'data-slug': 'source' + } + ) + ) + + disk_over_commit = forms.BooleanField( + label=_("Disk Over Commit"), + initial=False, + required=False, + widget=forms.CheckboxInput( + attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-live_migrate': _('Disk Over Commit') + } + ) + ) + + block_migration = forms.BooleanField( + label=_("Block Migration"), + initial=False, + required=False, + widget=forms.CheckboxInput( + attrs={ + 'class': 'switched', + 'data-switch-on': 'source', + 'data-source-live_migrate': _('Block Migration') + } + ) + ) + + def handle(self, request, data): + try: + current_host = data['current_host'] + migrate_type = data['migrate_type'] + disk_over_commit = data['disk_over_commit'] + block_migration = data['block_migration'] + live_migrate = migrate_type == 'live_migrate' + api.nova.migrate_host( + request, + current_host, + live_migrate=live_migrate, + disk_over_commit=disk_over_commit, + block_migration=block_migration + ) + msg = _('Starting to migrate host: %(current)s') % \ + {'current': current_host} + messages.success(request, msg) + return True + except Exception: + msg = _('Failed to migrate host "%s".') % data['current_host'] + redirect = reverse('horizon:admin:hypervisors:index') + exceptions.handle(request, message=msg, redirect=redirect) + return False diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/compute/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/compute/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/compute/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/compute/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -83,6 +83,36 @@ api.nova.service_enable(request, obj_id, 'nova-compute') +class MigrateMaintenanceHost(tables.LinkAction): + name = "migrate_maintenance" + policy_rules = (("compute", "compute_extension:admin_actions:migrate"),) + classes = ('ajax-modal', 'btn-migrate', 'btn-danger') + verbose_name = _("Migrate Host") + url = "horizon:admin:hypervisors:compute:migrate_host" + + @staticmethod + def action_present(count): + return ungettext_lazy( + u"Migrate Host", + u"Migrate Hosts", + count + ) + + @staticmethod + def action_past(count): + return ungettext_lazy( + u"Migrated Host", + u"Migrated Hosts", + count + ) + + def allowed(self, request, service): + if not api.nova.extension_supported('AdminActions', request): + return False + + return service.status == "disabled" + + class ComputeHostFilterAction(tables.FilterAction): def filter(self, table, services, filter_string): q = filter_string.lower() @@ -111,4 +141,9 @@ verbose_name = _("Compute Host") table_actions = (ComputeHostFilterAction,) multi_select = False - row_actions = (EvacuateHost, DisableService, EnableService) + row_actions = ( + EvacuateHost, + DisableService, + EnableService, + MigrateMaintenanceHost + ) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/compute/tests.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/compute/tests.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/compute/tests.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/compute/tests.py 2015-03-19 19:07:23.000000000 +0000 @@ -97,6 +97,98 @@ self.assertRedirectsNoFollow(res, dest_url) +class MigrateHostViewTest(test.BaseAdminViewTests): + def test_index(self): + disabled_services = [service for service in self.services.list() + if service.binary == 'nova-compute' + and service.status == 'disabled'] + disabled_service = disabled_services[0] + self.mox.ReplayAll() + url = reverse('horizon:admin:hypervisors:compute:migrate_host', + args=[disabled_service.host]) + res = self.client.get(url) + self.assertNoMessages() + self.assertTemplateUsed(res, + 'admin/hypervisors/compute/migrate_host.html') + + @test.create_stubs({api.nova: ('migrate_host',)}) + def test_maintenance_host_cold_migration_suceed(self): + disabled_services = [service for service in self.services.list() + if service.binary == 'nova-compute' + and service.status == 'disabled'] + disabled_service = disabled_services[0] + api.nova.migrate_host( + IsA(http.HttpRequest), + disabled_service.host, + live_migrate=False, + disk_over_commit=False, + block_migration=False + ).AndReturn(True) + self.mox.ReplayAll() + url = reverse('horizon:admin:hypervisors:compute:migrate_host', + args=[disabled_service.host]) + form_data = {'current_host': disabled_service.host, + 'migrate_type': 'cold_migrate', + 'disk_over_commit': False, + 'block_migration': False} + res = self.client.post(url, form_data) + dest_url = reverse('horizon:admin:hypervisors:index') + self.assertNoFormErrors(res) + self.assertMessageCount(success=1) + self.assertRedirectsNoFollow(res, dest_url) + + @test.create_stubs({api.nova: ('migrate_host',)}) + def test_maintenance_host_live_migration_succeed(self): + disabled_services = [service for service in self.services.list() + if service.binary == 'nova-compute' + and service.status == 'disabled'] + disabled_service = disabled_services[0] + api.nova.migrate_host( + IsA(http.HttpRequest), + disabled_service.host, + live_migrate=True, + disk_over_commit=False, + block_migration=True + ).AndReturn(True) + self.mox.ReplayAll() + url = reverse('horizon:admin:hypervisors:compute:migrate_host', + args=[disabled_service.host]) + form_data = {'current_host': disabled_service.host, + 'migrate_type': 'live_migrate', + 'disk_over_commit': False, + 'block_migration': True} + res = self.client.post(url, form_data) + dest_url = reverse('horizon:admin:hypervisors:index') + self.assertNoFormErrors(res) + self.assertMessageCount(success=1) + self.assertRedirectsNoFollow(res, dest_url) + + @test.create_stubs({api.nova: ('migrate_host',)}) + def test_maintenance_host_migration_fails(self): + disabled_services = [service for service in self.services.list() + if service.binary == 'nova-compute' + and service.status == 'disabled'] + disabled_service = disabled_services[0] + api.nova.migrate_host( + IsA(http.HttpRequest), + disabled_service.host, + live_migrate=True, + disk_over_commit=False, + block_migration=True + ).AndRaise(self.exceptions.nova) + self.mox.ReplayAll() + url = reverse('horizon:admin:hypervisors:compute:migrate_host', + args=[disabled_service.host]) + form_data = {'current_host': disabled_service.host, + 'migrate_type': 'live_migrate', + 'disk_over_commit': False, + 'block_migration': True} + res = self.client.post(url, form_data) + dest_url = reverse('horizon:admin:hypervisors:index') + self.assertMessageCount(error=1) + self.assertRedirectsNoFollow(res, dest_url) + + class DisableServiceViewTest(test.BaseAdminViewTests): @test.create_stubs({api.nova: ('hypervisor_list', 'hypervisor_stats')}) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/compute/urls.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/compute/urls.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/compute/urls.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/compute/urls.py 2015-03-19 19:07:23.000000000 +0000 @@ -24,4 +24,7 @@ url(r'^(?P[^/]+)/disable_service$', views.DisableServiceView.as_view(), name='disable_service'), + url(r'^(?P[^/]+)/migrate_host$', + views.MigrateHostView.as_view(), + name='migrate_host'), ) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/compute/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/compute/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/compute/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/compute/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -27,6 +27,7 @@ template_name = 'admin/hypervisors/compute/evacuate_host.html' context_object_name = 'compute_host' success_url = reverse_lazy("horizon:admin:hypervisors:index") + page_title = _("Evacuate Host") def get_context_data(self, **kwargs): context = super(EvacuateHostView, self).get_context_data(**kwargs) @@ -58,6 +59,7 @@ template_name = 'admin/hypervisors/compute/disable_service.html' context_object_name = 'compute_host' success_url = reverse_lazy("horizon:admin:hypervisors:index") + page_title = _("Disable Service") def get_context_data(self, **kwargs): context = super(DisableServiceView, self).get_context_data(**kwargs) @@ -68,3 +70,27 @@ initial = super(DisableServiceView, self).get_initial() initial.update({'host': self.kwargs['compute_host']}) return initial + + +class MigrateHostView(forms.ModalFormView): + form_class = project_forms.MigrateHostForm + template_name = 'admin/hypervisors/compute/migrate_host.html' + context_object_name = 'compute_host' + success_url = reverse_lazy("horizon:admin:hypervisors:index") + + def get_context_data(self, **kwargs): + context = super(MigrateHostView, self).get_context_data(**kwargs) + context["compute_host"] = self.kwargs['compute_host'] + return context + + def get_initial(self): + initial = super(MigrateHostView, self).get_initial() + current_host = self.kwargs['compute_host'] + + initial.update({ + 'current_host': current_host, + 'live_migrate': True, + 'block_migration': False, + 'disk_over_commit': False + }) + return initial diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -20,7 +20,7 @@ class AdminHypervisorsTable(tables.DataTable): hostname = tables.Column("hypervisor_hostname", - link=("horizon:admin:hypervisors:detail"), + link="horizon:admin:hypervisors:detail", attrs={'data-type': 'naturalSort'}, verbose_name=_("Hostname")) @@ -57,7 +57,8 @@ verbose_name=_("Instances")) def get_object_id(self, hypervisor): - return hypervisor.hypervisor_hostname + return "%s_%s" % (hypervisor.id, + hypervisor.hypervisor_hostname) class Meta(object): name = "hypervisors" @@ -66,7 +67,7 @@ class AdminHypervisorInstancesTable(tables.DataTable): name = tables.Column("name", - link=("horizon:admin:instances:detail"), + link="horizon:admin:instances:detail", verbose_name=_("Instance Name")) instance_id = tables.Column("uuid", diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/disable_service.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/disable_service.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/disable_service.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/disable_service.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Disable Service" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Disable Service") %} -{% endblock page_header %} - {% block main %} {% include 'admin/hypervisors/compute/_disable_service.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/evacuate_host.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/evacuate_host.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/evacuate_host.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/evacuate_host.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Evacuate Host" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Evacuate Host") %} -{% endblock page_header %} - {% block main %} {% include 'admin/hypervisors/compute/_evacuate_host.html' %} -{% endblock %} \ No newline at end of file +{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/_migrate_host.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/_migrate_host.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/_migrate_host.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/_migrate_host.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,24 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} +{% load url from future %} + +{% block form_id %}evacuate_host_form{% endblock %} +{% block form_action %}{% url 'horizon:admin:hypervisors:compute:migrate_host' compute_host %}{% endblock %} + +{% block modal-header %}{% trans "Migrate Host" %}{% endblock %} +{% block modal-body %} +
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+
+

{% trans "Description:" %}

+

{% trans "Migrate all instances from a host with disabled nova-compute service. Optionally you can choose type of migration. All running instances of the host can be Live Migrated. Cold Migration is trying to use 'nova migrate' on each instance of migrated host." %}

+
+{% endblock %} + +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/migrate_host.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/migrate_host.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/migrate_host.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/compute/migrate_host.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Migrate Host" %}{% endblock %} + +{% block page_header %} +{% include "horizon/common/_page_header.html" with title=_("Migrate Host") %} +{% endblock page_header %} + +{% block main %} + +{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/detail.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/detail.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/detail.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/detail.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Hypervisor Servers" %}{% endblock %} -{% block page_header %} -{% include "horizon/common/_page_header.html" with title=_("Hypervisor Servers") %} -{% endblock page_header %} - {% block main %}
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -3,10 +3,6 @@ {% block title %}{% trans "Hypervisors" %}{% endblock %} -{% block page_header %} -{% include "horizon/common/_page_header.html" with title=_("All Hypervisors") %} -{% endblock page_header %} - {% block main %}

{% trans "Hypervisor Summary" %}

diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/tests.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/tests.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/tests.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/tests.py 2015-03-19 19:07:23.000000000 +0000 @@ -55,6 +55,17 @@ self.assertEqual(2, len(actions_host_down)) self.assertEqual('evacuate', actions_host_down[0].name) + actions_service_enabled = host_table.get_row_actions( + host_table.data[1]) + self.assertEqual('evacuate', actions_service_enabled[0].name) + self.assertEqual('disable', actions_service_enabled[1].name) + + actions_service_disabled = host_table.get_row_actions( + host_table.data[2]) + self.assertEqual('enable', actions_service_disabled[0].name) + self.assertEqual('migrate_maintenance', + actions_service_disabled[1].name) + @test.create_stubs({api.nova: ('hypervisor_list', 'hypervisor_stats', 'service_list')}) @@ -77,12 +88,17 @@ class HypervisorDetailViewTest(test.BaseAdminViewTests): @test.create_stubs({api.nova: ('hypervisor_search',)}) def test_index(self): - hypervisor = self.hypervisors.list().pop().hypervisor_hostname + hypervisor = self.hypervisors.first() api.nova.hypervisor_search( - IsA(http.HttpRequest), hypervisor).AndReturn([]) + IsA(http.HttpRequest), + hypervisor.hypervisor_hostname).AndReturn([ + hypervisor, + self.hypervisors.list()[1]]) self.mox.ReplayAll() - url = reverse('horizon:admin:hypervisors:detail', args=[hypervisor]) + url = reverse('horizon:admin:hypervisors:detail', + args=["%s_%s" % (hypervisor.id, + hypervisor.hypervisor_hostname)]) res = self.client.get(url) self.assertTemplateUsed(res, 'admin/hypervisors/detail.html') - self.assertItemsEqual(res.context['table'].data, []) + self.assertItemsEqual(res.context['table'].data, hypervisor.servers) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/hypervisors/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/hypervisors/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -29,6 +29,7 @@ class AdminIndexView(tabs.TabbedTableView): tab_group_class = project_tabs.HypervisorHostTabs template_name = 'admin/hypervisors/index.html' + page_title = _("All Hypervisors") def get_data(self): hypervisors = [] @@ -55,17 +56,20 @@ class AdminDetailView(tables.DataTableView): table_class = project_tables.AdminHypervisorInstancesTable template_name = 'admin/hypervisors/detail.html' + page_title = _("Hypervisor Servers") def get_data(self): instances = [] try: + id, name = self.kwargs['hypervisor'].split('_', 1) result = api.nova.hypervisor_search(self.request, - self.kwargs['hypervisor']) + name) for hypervisor in result: - try: - instances += hypervisor.servers - except AttributeError: - pass + if str(hypervisor.id) == id: + try: + instances += hypervisor.servers + except AttributeError: + pass except Exception: exceptions.handle( self.request, diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/templates/images/_create.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/templates/images/_create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/templates/images/_create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/templates/images/_create.html 2015-03-19 19:07:23.000000000 +0000 @@ -3,6 +3,7 @@ {% load url from future %} {% block form_id %}create_image_form{% endblock %} +{% block ng_controller %}ImageFormCtrl{% endblock %} {% block form_action %}{% url 'horizon:admin:images:create' %}{% endblock %} {% block form_attrs %}enctype="multipart/form-data"{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/templates/images/create.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/templates/images/create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/templates/images/create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/templates/images/create.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create An Image" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create An Image") %} -{% endblock page_header %} - {% block main %} {% include 'admin/images/_create.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/templates/images/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/templates/images/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/templates/images/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/templates/images/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Images" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Images") %} -{% endblock page_header %} - {% block main %} {{ table.render }} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/templates/images/update.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/templates/images/update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/templates/images/update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/templates/images/update.html 2015-03-19 19:07:23.000000000 +0000 @@ -3,10 +3,6 @@ {% block title %}{% trans "Update Image" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Image") %} -{% endblock page_header %} - {% block main %} {% include 'admin/images/_update.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/templates/images/update_metadata.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/templates/images/update_metadata.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/templates/images/update_metadata.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/templates/images/update_metadata.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Update Image Metadata" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Image Metadata") %} -{% endblock page_header %} - {% block main %} {% include 'admin/images/_update_metadata.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/images/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/images/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -19,6 +19,8 @@ import json import logging +from oslo_utils import units + from django import conf from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse_lazy @@ -42,6 +44,7 @@ class IndexView(tables.DataTableView): table_class = project_tables.AdminImagesTable template_name = 'admin/images/index.html' + page_title = _("Images") def has_prev_data(self, table): return self._prev @@ -92,7 +95,7 @@ invalid_msg = ('API query is not valid and is ignored: %s=%s' % (filter_field, filter_string)) try: - filter_string = long(float(filter_string) * (1024 ** 2)) + filter_string = long(float(filter_string) * (units.Mi)) if filter_string >= 0: filters[filter_field] = filter_string else: @@ -108,12 +111,14 @@ template_name = 'admin/images/create.html' form_class = project_forms.AdminCreateImageForm success_url = reverse_lazy('horizon:admin:images:index') + page_title = _("Create An Image") class UpdateView(views.UpdateView): template_name = 'admin/images/update.html' form_class = project_forms.AdminUpdateImageForm success_url = reverse_lazy('horizon:admin:images:index') + page_title = _("Update Image") class DetailView(views.DetailView): @@ -125,6 +130,7 @@ template_name = "admin/images/update_metadata.html" form_class = project_forms.UpdateMetadataForm success_url = reverse_lazy('horizon:admin:images:index') + page_title = _("Update Image Metadata") def get_initial(self): image = self.get_object() diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -27,6 +27,11 @@ (SERVICE_DISABLED, _("Disabled")), ) +SERVICE_STATE_DISPLAY_CHOICES = ( + ('up', _("Up")), + ('down', _("Down")), +) + class ServiceFilterAction(tables.FilterAction): filter_field = 'type' @@ -76,7 +81,7 @@ return zone.zoneState['available'] -def get_nova_agent_status(agent): +def get_agent_status(agent): template_name = 'admin/info/_cell_status.html' context = { 'status': agent.status, @@ -89,9 +94,9 @@ binary = tables.Column("binary", verbose_name=_('Name')) host = tables.Column('host', verbose_name=_('Host')) zone = tables.Column('zone', verbose_name=_('Zone')) - status = tables.Column(get_nova_agent_status, verbose_name=_('Status')) + status = tables.Column(get_agent_status, verbose_name=_('Status')) state = tables.Column('state', verbose_name=_('State'), - filters=(filters.title,)) + display_choices=SERVICE_STATE_DISPLAY_CHOICES) updated_at = tables.Column('updated_at', verbose_name=pgettext_lazy( 'Time since the last update', @@ -113,10 +118,9 @@ binary = tables.Column("binary", verbose_name=_('Name')) host = tables.Column('host', verbose_name=_('Host')) zone = tables.Column('zone', verbose_name=_('Zone')) - status = tables.Column('status', verbose_name=_('Status'), - filters=(filters.title, )) + status = tables.Column(get_agent_status, verbose_name=_('Status')) state = tables.Column('state', verbose_name=_('State'), - filters=(filters.title, )) + display_choices=SERVICE_STATE_DISPLAY_CHOICES) updated_at = tables.Column('updated_at', verbose_name=pgettext_lazy( 'Time since the last update', @@ -181,3 +185,43 @@ verbose_name = _("Network Agents") table_actions = (NetworkAgentsFilterAction,) multi_select = False + + +class HeatServiceFilterAction(tables.FilterAction): + filter_field = 'type' + + def filter(self, table, services, filter_string): + q = filter_string.lower() + + def comp(service): + attr = getattr(service, self.filter_field, '') + if attr is not None and q in attr.lower(): + return True + return False + + return filter(comp, services) + + +class HeatServiceTable(tables.DataTable): + hostname = tables.Column('hostname', verbose_name=_('Hostname')) + binary = tables.Column("binary", verbose_name=_('Name')) + engine_id = tables.Column('engine_id', verbose_name=_('Engine Id')) + host = tables.Column('host', verbose_name=_('Host')) + topic = tables.Column('topic', verbose_name=_('Topic')) + updated_at = tables.Column('updated_at', + verbose_name=pgettext_lazy( + 'Time since the last update', + u'Last Updated'), + filters=(utils_filters.parse_isotime, + filters.timesince)) + status = tables.Column('status', verbose_name=_('Status'), + display_choices=SERVICE_STATE_DISPLAY_CHOICES) + + def get_object_id(self, obj): + return "%s" % obj.engine_id + + class Meta(object): + name = "heat_services" + verbose_name = _("Orchestration Services") + table_actions = (HeatServiceFilterAction,) + multi_select = False diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/tabs.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/tabs.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/tabs.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/tabs.py 2015-03-19 19:07:23.000000000 +0000 @@ -18,6 +18,7 @@ from horizon import tabs from openstack_dashboard.api import base from openstack_dashboard.api import cinder +from openstack_dashboard.api import heat from openstack_dashboard.api import keystone from openstack_dashboard.api import neutron from openstack_dashboard.api import nova @@ -102,8 +103,32 @@ return agents +class HeatServiceTab(tabs.TableTab): + table_classes = (tables.HeatServiceTable,) + name = tables.HeatServiceTable.Meta.verbose_name + slug = tables.HeatServiceTable.Meta.name + template_name = constants.INFO_DETAIL_TEMPLATE_NAME + + def allowed(self, request): + try: + return (base.is_service_enabled(request, 'orchestration')) + except Exception: + exceptions.handle(request, _('Orchestration service is disabled.')) + return False + + def get_heat_services_data(self): + try: + services = heat.service_list(self.tab_group.request) + except Exception: + msg = _('Unable to get Orchestration service list.') + exceptions.check_message(["Connection", "refused"], msg) + exceptions.handle(self.request, msg) + services = [] + return services + + class SystemInfoTabs(tabs.TabGroup): slug = "system_info" tabs = (ServicesTab, NovaServicesTab, CinderServicesTab, - NetworkAgentsTab) + NetworkAgentsTab, HeatServiceTab) sticky = True diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/templates/info/_cell_status.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/templates/info/_cell_status.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/templates/info/_cell_status.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/templates/info/_cell_status.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,5 +1,9 @@ {% load i18n %} -{{ status|title }} -{% if status == 'disabled' and disabled_reason %} -
{% blocktrans %}Reason: {{ disabled_reason }}{% endblocktrans %} +{% if status == 'disabled' %} + {% trans "Disabled"%} + {% if disabled_reason %} +
{% blocktrans %}Reason: {{ disabled_reason }}{% endblocktrans %} + {% endif %} +{% else %} + {% trans "Enabled"%} {% endif %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/templates/info/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/templates/info/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/templates/info/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/templates/info/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "System Information" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("System Information")%} -{% endblock page_header %} - {% block main %}
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/tests.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/tests.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/tests.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/tests.py 2015-03-19 19:07:23.000000000 +0000 @@ -28,12 +28,15 @@ @test.create_stubs({api.base: ('is_service_enabled',), api.nova: ('service_list',), api.neutron: ('agent_list', 'is_extension_supported'), - api.cinder: ('service_list',)}) - def test_index(self): - services = self.services.list() - api.nova.service_list(IsA(http.HttpRequest)).AndReturn(services) + api.cinder: ('service_list',), + api.heat: ('service_list',)}) + def _test_base_index(self): api.base.is_service_enabled(IsA(http.HttpRequest), IgnoreArg()) \ .MultipleTimes().AndReturn(True) + + services = self.services.list() + api.nova.service_list(IsA(http.HttpRequest)).AndReturn(services) + api.neutron.is_extension_supported(IsA(http.HttpRequest), 'agent').AndReturn(True) agents = self.agents.list() @@ -43,55 +46,65 @@ api.cinder.service_list(IsA(http.HttpRequest)).\ AndReturn(cinder_services) + heat_services = self.heat_services.list() + api.heat.service_list(IsA(http.HttpRequest)).\ + AndReturn(heat_services) + self.mox.ReplayAll() res = self.client.get(INDEX_URL) - self.assertTemplateUsed(res, 'admin/info/index.html') + return res + + def test_index(self): + res = self._test_base_index() services_tab = res.context['tab_group'].get_tab('services') - self.assertQuerysetEqual(services_tab._tables['services'].data, - ['', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', ]) + self.assertQuerysetEqual( + services_tab._tables['services'].data, + ['', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', ]) + + self.mox.VerifyAll() + def test_neutron_index(self): + res = self._test_base_index() network_agents_tab = res.context['tab_group'].get_tab('network_agents') self.assertQuerysetEqual( network_agents_tab._tables['network_agents'].data, [agent.__repr__() for agent in self.agents.list()] ) - self.mox.VerifyAll() - @test.create_stubs({api.base: ('is_service_enabled',), - api.cinder: ('service_list',), - api.nova: ('service_list',), - api.neutron: ('agent_list', 'is_extension_supported')}) - def test_cinder_services_index(self): - cinder_services = self.cinder_services.list() - api.nova.service_list(IsA(http.HttpRequest)).AndReturn([]) - api.cinder.service_list(IsA(http.HttpRequest)).\ - AndReturn(cinder_services) - api.neutron.agent_list(IsA(http.HttpRequest)).AndReturn([]) - api.base.is_service_enabled(IsA(http.HttpRequest), IgnoreArg()) \ - .MultipleTimes().AndReturn(True) - api.neutron.is_extension_supported(IsA(http.HttpRequest), - 'agent').AndReturn(True) + self.mox.VerifyAll() - self.mox.ReplayAll() - res = self.client.get(INDEX_URL) + def test_cinder_index(self): + res = self._test_base_index() cinder_services_tab = res.context['tab_group'].\ get_tab('cinder_services') + self.assertQuerysetEqual( + cinder_services_tab._tables['cinder_services'].data, + [service.__repr__() for service in self.cinder_services.list()] + ) - self.assertTemplateUsed(res, 'admin/info/index.html') - self.assertQuerysetEqual(cinder_services_tab._tables - ['cinder_services'].data, - ['', - '']) + self.mox.VerifyAll() + + def test_heat_index(self): + res = self._test_base_index() + heat_services_tab = res.context['tab_group'].\ + get_tab('heat_services') + self.assertQuerysetEqual( + heat_services_tab._tables['heat_services'].data, + [service.__repr__() for service in self.heat_services.list()] + ) + + self.mox.VerifyAll() diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/info/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/info/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -29,6 +29,7 @@ class IndexView(tabs.TabbedTableView): tab_group_class = project_tabs.SystemInfoTabs template_name = constants.INFO_TEMPLATE_NAME + page_title = _("System Information") def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/instances/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/instances/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/instances/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/instances/tables.py 2015-03-19 19:07:29.000000000 +0000 @@ -34,6 +34,7 @@ name = "migrate" classes = ("btn-migrate", "btn-danger") policy_rules = (("compute", "compute_extension:admin_actions:migrate"),) + help_text = _("Migrating instances may cause some unrecoverable results.") @staticmethod def action_present(count): @@ -90,7 +91,7 @@ # session property used for persisting the filter. name = "filter_admin_instances" filter_type = "server" - filter_choices = (('project', _("Project"), True), + filter_choices = (('project', _("Project ="), True), ('host', _("Host ="), True), ('name', _("Name"), True), ('ip', _("IPv4 Address ="), True), @@ -125,7 +126,7 @@ verbose_name=_("Host"), classes=('nowrap-col',)) name = tables.Column("name", - link=("horizon:admin:instances:detail"), + link="horizon:admin:instances:detail", verbose_name=_("Name")) image_name = tables.Column("image_name", verbose_name=_("Image Name")) @@ -134,7 +135,6 @@ attrs={'data-type': "ip"}) size = tables.Column(project_tables.get_size, verbose_name=_("Size"), - classes=('nowrap-col',), attrs={'data-type': 'size'}) status = tables.Column( "status", diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/instances/templates/instances/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/instances/templates/instances/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/instances/templates/instances/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/instances/templates/instances/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Instances" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("All Instances") %} -{% endblock page_header %} - {% block main %} {{ table.render }} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/instances/templates/instances/live_migrate.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/instances/templates/instances/live_migrate.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/instances/templates/instances/live_migrate.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/instances/templates/instances/live_migrate.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Live Migrate" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Live Migrate") %} -{% endblock page_header %} - {% block main %} {% include 'admin/instances/_live_migrate.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/instances/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/instances/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/instances/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/instances/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -64,6 +64,7 @@ class AdminIndexView(tables.DataTableView): table_class = project_tables.AdminInstancesTable template_name = 'admin/instances/index.html' + page_title = _("Instances") def has_more_data(self, table): return self._more @@ -152,6 +153,7 @@ template_name = 'admin/instances/live_migrate.html' context_object_name = 'instance' success_url = reverse_lazy("horizon:admin:instances:index") + page_title = _("Live Migrate") def get_context_data(self, **kwargs): context = super(LiveMigrateView, self).get_context_data(**kwargs) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metadata_defs/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metadata_defs/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metadata_defs/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metadata_defs/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -133,7 +133,7 @@ form_field=forms.CharField(widget=forms.Textarea(), required=False), truncate=200) resource_type_names = tables.Column( - "resource_type_names", + lambda obj: getattr(obj, 'resource_type_names', []), verbose_name=_("Resource Types"), wrap_list=True, filters=(filters.unordered_list,)) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/create.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/create.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create a Metadata Namespace" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create a Metadata Namespace") %} -{% endblock page_header %} - {% block main %} {% include 'admin/metadata_defs/_create.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/detail.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/detail.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/detail.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/detail.html 2015-03-19 19:07:23.000000000 +0000 @@ -3,10 +3,6 @@ {% block title %}{% trans "Namespace Details" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Namespace Details: ")|add:namespace.namespace|default:_("None") %} -{% endblock page_header %} - {% block main %}
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metadata_defs/templates/metadata_defs/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Metadata Definitions" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Metadata Definitions") %} -{% endblock page_header %} - {% block main %} {{ table.render }} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metadata_defs/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metadata_defs/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metadata_defs/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metadata_defs/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -37,6 +37,7 @@ class AdminIndexView(tables.DataTableView): table_class = admin_tables.AdminNamespacesTable template_name = constants.METADATA_INDEX_TEMPLATE + page_title = _("Metadata Definitions") def has_prev_data(self, table): return self._prev @@ -82,13 +83,14 @@ template_name = constants.METADATA_CREATE_TEMPLATE context_object_name = 'namespace' success_url = reverse_lazy(constants.METADATA_INDEX_URL) + page_title = _("Create a Metadata Namespace") class DetailView(tabs.TabView): redirect_url = constants.METADATA_INDEX_URL - tab_group_class = admin_tabs.NamespaceDetailTabs template_name = constants.METADATA_DETAIL_TEMPLATE + page_title = _("Namespace Details: {{ namespace.namespace }}") def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metering/panel.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metering/panel.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metering/panel.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metering/panel.py 2015-03-19 19:07:23.000000000 +0000 @@ -19,7 +19,10 @@ class Metering(horizon.Panel): name = _("Resource Usage") slug = 'metering' - permissions = ('openstack.services.metering', 'openstack.roles.admin', ) + permissions = ('openstack.services.metering', ) + policy_rules = (('identity', 'identity:list_projects'), + ('telemetry', 'telemetry:compute_statistics'), + ('telemetry', 'telemetry:get_meter'),) dashboard.Admin.register(Metering) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metering/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metering/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metering/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metering/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -25,7 +25,7 @@ name = "create" verbose_name = _("Modify Usage Report Parameters") url = "horizon:admin:metering:create" - classes = ("btn-create",) + classes = ("ajax-modal",) icon = "edit" diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metering/templates/metering/daily.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metering/templates/metering/daily.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metering/templates/metering/daily.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metering/templates/metering/daily.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Modify Usage Report Parameters" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Modify Usage Report Parameters") %} -{% endblock page_header %} - {% block main %} {% include "admin/metering/_daily.html" %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metering/templates/metering/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metering/templates/metering/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metering/templates/metering/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metering/templates/metering/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Resources Usage Overview" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Resources Usage Overview")%} -{% endblock page_header %} - {% block main %}
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metering/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metering/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/metering/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/metering/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -38,12 +38,14 @@ class IndexView(tabs.TabbedTableView): tab_group_class = metering_tabs.CeilometerOverviewTabs template_name = 'admin/metering/index.html' + page_title = _("Resources Usage Overview") class CreateUsageReport(forms.ModalFormView): form_class = metering_forms.UsageReportForm template_name = 'admin/metering/daily.html' success_url = reverse_lazy('horizon:admin:metering:index') + page_title = _("Modify Usage Report Parameters") class SamplesView(django.views.generic.TemplateView): diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/agents/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/agents/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/agents/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/agents/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -27,9 +27,13 @@ class AddView(forms.ModalFormView): form_class = project_forms.AddDHCPAgent + form_id = "add_dhcp_agent_form" template_name = 'admin/networks/agents/add.html' success_url = 'horizon:admin:networks:detail' failure_url = 'horizon:admin:networks:detail' + submit_url = "horizon:admin:networks:adddhcpagent" + title_and_label = _("Add DHCP Agent") + submit_label = modal_header = page_title = title_and_label def get_success_url(self): return reverse(self.success_url, @@ -38,6 +42,8 @@ def get_context_data(self, **kwargs): context = super(AddView, self).get_context_data(**kwargs) context['network_id'] = self.kwargs['network_id'] + args = (self.kwargs['network_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) return context def get_initial(self): diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/ports/forms.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/ports/forms.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/ports/forms.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/ports/forms.py 2015-03-19 19:07:29.000000000 +0000 @@ -14,6 +14,7 @@ import logging +from django.conf import settings from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ @@ -27,6 +28,8 @@ LOG = logging.getLogger(__name__) +VNIC_TYPES = [('normal', _('Normal')), ('direct', _('Direct')), + ('macvtap', _('MacVTap'))] class CreatePort(forms.SelfHandlingForm): @@ -49,9 +52,36 @@ help_text=_("Device owner attached to the " "port"), required=False) + binding__host_id = forms.CharField( + label=_("Binding: Host"), + help_text=_("The ID of the host where the port is allocated. In some " + "cases, different implementations can run on different " + "hosts."), + required=False) + + failure_url = 'horizon:admin:networks:detail' def __init__(self, request, *args, **kwargs): super(CreatePort, self).__init__(request, *args, **kwargs) + if api.neutron.is_extension_supported(request, 'binding'): + neutron_settings = getattr(settings, + 'OPENSTACK_NEUTRON_NETWORK', {}) + supported_vnic_types = neutron_settings.get( + 'supported_vnic_types', ['*']) + if supported_vnic_types == ['*']: + vnic_type_choices = VNIC_TYPES + else: + vnic_type_choices = [ + vnic_type for vnic_type in VNIC_TYPES + if vnic_type[0] in supported_vnic_types + ] + + self.fields['binding__vnic_type'] = forms.ChoiceField( + choices=vnic_type_choices, + label=_("Binding: VNIC Type"), + help_text=_("The VNIC type that is bound to the neutron port"), + required=False) + if api.neutron.is_extension_supported(request, 'mac-learning'): self.fields['mac_state'] = forms.BooleanField( label=_("MAC Learning State"), initial=False, required=False) @@ -78,7 +108,7 @@ msg = _('Failed to create a port for network %s') \ % data['network_id'] LOG.info(msg) - redirect = reverse('horizon:admin:networks:detail', + redirect = reverse(self.failure_url, args=(data['network_id'],)) exceptions.handle(request, msg, redirect=redirect) @@ -92,6 +122,13 @@ help_text=_("Device owner attached to the " "port"), required=False) + binding__host_id = forms.CharField( + label=_("Binding: Host"), + help_text=_("The ID of the host where the port is allocated. In some " + "cases, different implementations can run on different " + "hosts."), + required=False) + failure_url = 'horizon:admin:networks:detail' def handle(self, request, data): @@ -99,13 +136,21 @@ LOG.debug('params = %s' % data) extension_kwargs = {} data['admin_state'] = (data['admin_state'] == 'True') + if 'binding__vnic_type' in data: + extension_kwargs['binding__vnic_type'] = \ + data['binding__vnic_type'] + if 'mac_state' in data: extension_kwargs['mac_learning_enabled'] = data['mac_state'] - port = api.neutron.port_update(request, data['port_id'], + + port = api.neutron.port_update(request, + data['port_id'], name=data['name'], admin_state_up=data['admin_state'], device_id=data['device_id'], device_owner=data['device_owner'], + binding__host_id=data + ['binding__host_id'], **extension_kwargs) msg = _('Port %s was successfully updated.') % data['port_id'] LOG.debug(msg) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/ports/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/ports/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/ports/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/ports/tables.py 2015-03-19 19:07:29.000000000 +0000 @@ -73,36 +73,14 @@ return reverse(self.url, args=(network_id,)) -class UpdatePort(policy.PolicyTargetMixin, tables.LinkAction): - name = "update" - verbose_name = _("Edit Port") +class UpdatePort(project_tables.UpdatePort): url = "horizon:admin:networks:editport" - classes = ("ajax-modal",) - icon = "pencil" - policy_rules = (("network", "update_port"),) - def get_link_url(self, port): - network_id = self.table.kwargs['network_id'] - return reverse(self.url, args=(network_id, port.id)) - -class PortsTable(tables.DataTable): +class PortsTable(project_tables.PortsTable): name = tables.Column("name_or_id", verbose_name=_("Name"), link="horizon:admin:networks:ports:detail") - fixed_ips = tables.Column( - project_tables.get_fixed_ips, verbose_name=_("Fixed IPs")) - device_id = tables.Column( - project_tables.get_attached, verbose_name=_("Device Attached")) - status = tables.Column( - "status", - verbose_name=_("Status"), - display_choices=project_tables.STATUS_DISPLAY_CHOICES) - admin_state = tables.Column("admin_state", - verbose_name=_("Admin State"), - display_choices=project_tables.DISPLAY_CHOICES) - mac_state = tables.Column("mac_state", empty_value=api.neutron.OFF_STATE, - verbose_name=_("Mac Learning State")) class Meta(object): name = "ports" @@ -110,10 +88,3 @@ table_actions = (CreatePort, DeletePort) row_actions = (UpdatePort, DeletePort,) hidden_title = False - - def __init__(self, request, data=None, needs_form_wrapper=None, **kwargs): - super(PortsTable, self).__init__(request, data=data, - needs_form_wrapper=needs_form_wrapper, - **kwargs) - if not api.neutron.is_extension_supported(request, 'mac-learning'): - del self.columns['mac_state'] diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/ports/tabs.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/ports/tabs.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/ports/tabs.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/ports/tabs.py 2015-03-19 19:07:29.000000000 +0000 @@ -12,31 +12,13 @@ # License for the specific language governing permissions and limitations # under the License. -from django.core.urlresolvers import reverse -from django.utils.translation import ugettext_lazy as _ +from openstack_dashboard.dashboards.project.networks.ports \ + import tabs as project_tabs -from horizon import exceptions -from horizon import tabs -from openstack_dashboard import api +class OverviewTab(project_tabs.OverviewTab): + template_name = "admin/networks/ports/_detail_overview.html" -class OverviewTab(tabs.Tab): - name = _("Overview") - slug = "overview" - template_name = "project/networks/ports/_detail_overview.html" - - def get_context_data(self, request): - port_id = self.tab_group.kwargs['port_id'] - try: - port = api.neutron.port_get(self.request, port_id) - except Exception: - redirect = reverse('horizon:admin:networks:index') - msg = _('Unable to retrieve port details.') - exceptions.handle(request, msg, redirect=redirect) - return {'port': port} - - -class PortDetailTabs(tabs.TabGroup): - slug = "port_details" +class PortDetailTabs(project_tabs.PortDetailTabs): tabs = (OverviewTab,) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/ports/urls.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/ports/urls.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/ports/urls.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/ports/urls.py 2015-03-19 19:07:29.000000000 +0000 @@ -15,7 +15,7 @@ from django.conf.urls import patterns from django.conf.urls import url -from openstack_dashboard.dashboards.project.networks.ports import views +from openstack_dashboard.dashboards.admin.networks.ports import views PORTS = r'^(?P[^/]+)/%s$' VIEW_MOD = 'openstack_dashboard.dashboards.admin.networks.ports.views' diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/ports/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/ports/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/ports/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/ports/views.py 2015-03-19 19:07:29.000000000 +0000 @@ -20,21 +20,29 @@ from horizon.utils import memoized from openstack_dashboard import api -from openstack_dashboard.dashboards.project.networks.ports \ - import views as project_views from openstack_dashboard.dashboards.admin.networks.ports \ - import forms as project_forms + import forms as ports_forms +from openstack_dashboard.dashboards.admin.networks.ports \ + import tables as ports_tables +from openstack_dashboard.dashboards.admin.networks.ports \ + import tabs as ports_tabs +from openstack_dashboard.dashboards.project.networks.ports \ + import views as project_views class CreateView(forms.ModalFormView): - form_class = project_forms.CreatePort + form_class = ports_forms.CreatePort + form_id = "create_port_form" + modal_header = _("Create Port") + submit_label = _("Create Port") + submit_url = "horizon:admin:networks:addport" + page_title = _("Create Port") template_name = 'admin/networks/ports/create.html' - success_url = 'horizon:admin:networks:detail' - failure_url = 'horizon:admin:networks:detail' + url = 'horizon:admin:networks:detail' def get_success_url(self): - return reverse(self.success_url, + return reverse(self.url, args=(self.kwargs['network_id'],)) @memoized.memoized_method @@ -43,7 +51,7 @@ network_id = self.kwargs["network_id"] return api.neutron.network_get(self.request, network_id) except Exception: - redirect = reverse(self.failure_url, + redirect = reverse(self.url, args=(self.kwargs['network_id'],)) msg = _("Unable to retrieve network.") exceptions.handle(self.request, msg, redirect=redirect) @@ -51,6 +59,8 @@ def get_context_data(self, **kwargs): context = super(CreateView, self).get_context_data(**kwargs) context['network'] = self.get_object() + args = (self.kwargs['network_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) return context def get_initial(self): @@ -59,8 +69,32 @@ "network_name": network.name} +class DetailView(project_views.DetailView): + tab_group_class = ports_tabs.PortDetailTabs + + def get_context_data(self, **kwargs): + context = super(DetailView, self).get_context_data(**kwargs) + port = context["port"] + table = ports_tables.PortsTable(self.request, + network_id=port.network_id) + context["url"] = reverse('horizon:admin:networks:index') + context["actions"] = table.render_row_actions(port) + return context + + @staticmethod + def get_redirect_url(): + return reverse('horizon:admin:networks:index') + + class UpdateView(project_views.UpdateView): - form_class = project_forms.UpdatePort + form_class = ports_forms.UpdatePort template_name = 'admin/networks/ports/update.html' context_object_name = 'port' + submit_url = "horizon:admin:networks:editport" success_url = 'horizon:admin:networks:detail' + + def get_initial(self): + initial = super(UpdateView, self).get_initial() + port = self._get_object() + initial['binding__host_id'] = port['binding__host_id'] + return initial diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/agents/_add.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/agents/_add.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/agents/_add.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/agents/_add.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,26 +1,7 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}add_dhcp_agent_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:networks:adddhcpagent' network_id %} -{% endblock %} - -{% block modal-header %}{% trans "Add DHCP Agent" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

{% trans "From here you can add a DHCP agent for the network."%}

-
-{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "From here you can add a DHCP agent for the network."%}

{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/agents/add.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/agents/add.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/agents/add.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/agents/add.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Add DHCP Agent" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Add DHCP Agent") %} -{% endblock page_header %} - {% block main %} {% include "admin/networks/agents/_add.html" %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/create.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/create.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create Network" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create Network") %} -{% endblock page_header %} - {% block main %} {% include "admin/networks/_create.html" %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Networks" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Networks") %} -{% endblock page_header %} - {% block main %}
{{ networks_table.render }} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_create.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_create.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,26 +1,7 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}create_port_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:networks:addport' network.id %} -{% endblock %} - -{% block modal-header %}{% trans "Create Port" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

{% trans "You can create a port for the network. If you specify device ID to be attached, the device specified will be attached to the port created."%}

-
-{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "You can create a port for the network. If you specify device ID to be attached, the device specified will be attached to the port created."%}

{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/create.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/create.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create Port" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create Port") %} -{% endblock page_header %} - {% block main %} {% include "admin/networks/ports/_create.html" %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_detail_overview.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_detail_overview.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_detail_overview.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_detail_overview.html 2015-03-19 19:07:29.000000000 +0000 @@ -0,0 +1,73 @@ +{% load i18n sizeformat %} +{% load url from future %} + +
+
+
{% trans "Name" %}
+
{{ port.name|default:_("None") }}
+
{% trans "ID" %}
+
{{ port.id|default:_("None") }}
+ {% url 'horizon:project:networks:detail' port.network_id as network_url %} +
{% trans "Network ID" %}
+
{{ port.network_id|default:_("None") }}
+
{% trans "Project ID" %}
+
{{ port.tenant_id|default:_("-") }}
+
{% trans "MAC Address" %}
+
{{ port.mac_address|default:_("None") }}
+
{% trans "Status" %}
+
{{ port.status_label|default:_("None") }}
+
{% trans "Admin State" %}
+
{{ port.admin_state_label|default:_("None") }}
+ {% if port.mac_state %} +
{% trans "MAC Learning State" %}
+
{% trans "On" %}
+ {% endif %} +

{% trans "Fixed IP" %}

+
+ {% if port.fixed_ips.items|length > 1 %} + {% for ip in port.fixed_ips %} +
{% trans "IP Address" %}
+
{{ ip.ip_address }}
+ {% url 'horizon:project:networks:subnets:detail' ip.subnet_id as subnet_url %} +
{% trans "Subnet ID" %}
+
{{ ip.subnet_id }}
+ {% endfor %} + {% else %} +
{% trans "None" %}
+ {% endif %} +

{% trans "Attached Device" %}

+
+ {% if port.device_id|length > 1 or port.device_owner %} +
{% trans "Device Owner" %}
+
{{ port.device_owner|default:_("None") }}
+
{% trans "Device ID" %}
+
{{ port.device_id|default:_("None") }}
+ {% else %} +
{% trans "No attached device" %}
+ {% endif %} +

{% trans "Binding" %}

+
+
{% trans "Host" %}
+
{{ port.binding__host_id|default:_("None") }}
+
{% trans "Profile" %}
+
{{ port.binding__profile|default:_("None") }}
+
{% trans "VIF Type" %}
+
{{ port.binding__vif_type|replace_underscores }}
+
{% trans "VIF Details" %}
+ {% if port.binding__vif_details.items %} +
+
    + {% for key,value in port.binding__vif_details.items %} +
  • {{ key }} {{ value }}
  • + {% endfor %} +
+
+ {% else %} +
{% trans "None" %}
+ {% endif %} + {% if port.binding__vnic_type %} +
{% trans "VNIC Type" %}
+
{{ port.binding__vnic_type }}
+ {% endif %} +
+
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_update.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_update.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,30 +1,7 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}update_port_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:networks:editport' network_id port_id %}{% endblock %} - -{% block modal-header %}{% trans "Edit Port" %}{% endblock %} - -{% block modal-body %} -
-
-
{% trans "ID" %}
-
{{ port_id }}
-
-
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

{% trans "You may update the editable properties of your port here." %}

-
-{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "You may update the editable properties of your port here." %}

{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/update.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/update.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Update Port" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Port") %} -{% endblock page_header %} - {% block main %} {% include 'admin/networks/ports/_update.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/_update.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/_update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/_update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/_update.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,25 +1,7 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}update_network_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:networks:update' network_id %}{% endblock %} - -{% block modal-header %}{% trans "Edit Network" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

{% trans "You may update the editable properties of your network here." %}

-
-{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "You may update the editable properties of your network here." %}

{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/update.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/templates/networks/update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/templates/networks/update.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Update Network" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Network") %} -{% endblock page_header %} - {% block main %} {% include 'admin/networks/_update.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/tests.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/tests.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/tests.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/tests.py 2015-03-19 19:07:29.000000000 +0000 @@ -975,7 +975,9 @@ api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) - + api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'mac-learning')\ + .AndReturn(mac_learning) self.mox.ReplayAll() res = self.client.get(reverse('horizon:admin:networks:ports:detail', @@ -997,7 +999,7 @@ # admin DetailView is shared with userpanel one, so # redirection URL on error is userpanel index. - redir_url = reverse('horizon:project:networks:index') + redir_url = reverse('horizon:admin:networks:index') self.assertRedirectsNoFollow(res, redir_url) @test.create_stubs({api.neutron: ('network_get', @@ -1010,12 +1012,15 @@ def test_port_create_get_with_mac_learning(self): self._test_port_create_get(mac_learning=True) - def _test_port_create_get(self, mac_learning=False): + def _test_port_create_get(self, mac_learning=False, binding=False): network = self.networks.first() api.neutron.network_get(IsA(http.HttpRequest), network.id)\ .AndReturn(self.networks.first()) api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'binding')\ + .AndReturn(binding) + api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) self.mox.ReplayAll() @@ -1036,9 +1041,9 @@ 'is_extension_supported', 'port_create',)}) def test_port_create_post_with_mac_learning(self): - self._test_port_create_post(mac_learning=True) + self._test_port_create_post(mac_learning=True, binding=False) - def _test_port_create_post(self, mac_learning=False): + def _test_port_create_post(self, mac_learning=False, binding=False): network = self.networks.first() port = self.ports.first() api.neutron.network_get(IsA(http.HttpRequest), @@ -1048,9 +1053,15 @@ network.id)\ .AndReturn(self.networks.first()) api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'binding')\ + .AndReturn(binding) + api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) extension_kwargs = {} + if binding: + extension_kwargs['binding__vnic_type'] = \ + port.binding__vnic_type if mac_learning: extension_kwargs['mac_learning_enabled'] = True api.neutron.port_create(IsA(http.HttpRequest), @@ -1060,6 +1071,7 @@ admin_state_up=port.admin_state_up, device_id=port.device_id, device_owner=port.device_owner, + binding__host_id=port.binding__host_id, **extension_kwargs)\ .AndReturn(port) self.mox.ReplayAll() @@ -1069,7 +1081,10 @@ 'name': port.name, 'admin_state': port.admin_state_up, 'device_id': port.device_id, - 'device_owner': port.device_owner} + 'device_owner': port.device_owner, + 'binding__host_id': port.binding__host_id} + if binding: + form_data['binding__vnic_type'] = port.binding__vnic_type if mac_learning: form_data['mac_state'] = True url = reverse('horizon:admin:networks:addport', @@ -1093,7 +1108,8 @@ def test_port_create_post_exception_with_mac_learning(self): self._test_port_create_post_exception(mac_learning=True) - def _test_port_create_post_exception(self, mac_learning=False): + def _test_port_create_post_exception(self, mac_learning=False, + binding=False): network = self.networks.first() port = self.ports.first() api.neutron.network_get(IsA(http.HttpRequest), @@ -1103,9 +1119,14 @@ network.id)\ .AndReturn(self.networks.first()) api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'binding')\ + .AndReturn(binding) + api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) extension_kwargs = {} + if binding: + extension_kwargs['binding__vnic_type'] = port.binding__vnic_type if mac_learning: extension_kwargs['mac_learning_enabled'] = True api.neutron.port_create(IsA(http.HttpRequest), @@ -1115,6 +1136,7 @@ admin_state_up=port.admin_state_up, device_id=port.device_id, device_owner=port.device_owner, + binding__host_id=port.binding__host_id, **extension_kwargs)\ .AndRaise(self.exceptions.neutron) self.mox.ReplayAll() @@ -1125,7 +1147,10 @@ 'admin_state': port.admin_state_up, 'mac_state': True, 'device_id': port.device_id, - 'device_owner': port.device_owner} + 'device_owner': port.device_owner, + 'binding__host_id': port.binding__host_id} + if binding: + form_data['binding__vnic_type'] = port.binding__vnic_type if mac_learning: form_data['mac_learning_enabled'] = True url = reverse('horizon:admin:networks:addport', @@ -1147,12 +1172,15 @@ def test_port_update_get_with_mac_learning(self): self._test_port_update_get(mac_learning=True) - def _test_port_update_get(self, mac_learning=False): + def _test_port_update_get(self, mac_learning=False, binding=False): port = self.ports.first() api.neutron.port_get(IsA(http.HttpRequest), port.id)\ .AndReturn(port) api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'binding')\ + .AndReturn(binding) + api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) self.mox.ReplayAll() @@ -1175,14 +1203,19 @@ def test_port_update_post_with_mac_learning(self): self._test_port_update_post(mac_learning=True) - def _test_port_update_post(self, mac_learning=False): + def _test_port_update_post(self, mac_learning=False, binding=False): port = self.ports.first() api.neutron.port_get(IsA(http.HttpRequest), port.id)\ .AndReturn(port) api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'binding')\ + .AndReturn(binding) + api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) extension_kwargs = {} + if binding: + extension_kwargs['binding__vnic_type'] = port.binding__vnic_type if mac_learning: extension_kwargs['mac_learning_enabled'] = True api.neutron.port_update(IsA(http.HttpRequest), port.id, @@ -1190,6 +1223,7 @@ admin_state_up=port.admin_state_up, device_id=port.device_id, device_owner=port.device_owner, + binding__host_id=port.binding__host_id, **extension_kwargs)\ .AndReturn(port) self.mox.ReplayAll() @@ -1199,7 +1233,10 @@ 'name': port.name, 'admin_state': port.admin_state_up, 'device_id': port.device_id, - 'device_owner': port.device_owner} + 'device_owner': port.device_owner, + 'binding__host_id': port.binding__host_id} + if binding: + form_data['binding__vnic_type'] = port.binding__vnic_type if mac_learning: form_data['mac_state'] = True url = reverse('horizon:admin:networks:editport', @@ -1220,16 +1257,22 @@ 'is_extension_supported', 'port_update')}) def test_port_update_post_exception_with_mac_learning(self): - self._test_port_update_post_exception(mac_learning=True) + self._test_port_update_post_exception(mac_learning=True, binding=False) - def _test_port_update_post_exception(self, mac_learning=False): + def _test_port_update_post_exception(self, mac_learning=False, + binding=False): port = self.ports.first() api.neutron.port_get(IsA(http.HttpRequest), port.id)\ .AndReturn(port) api.neutron.is_extension_supported(IsA(http.HttpRequest), + 'binding')\ + .AndReturn(binding) + api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning')\ .AndReturn(mac_learning) extension_kwargs = {} + if binding: + extension_kwargs['binding__vnic_type'] = port.binding__vnic_type if mac_learning: extension_kwargs['mac_learning_enabled'] = True api.neutron.port_update(IsA(http.HttpRequest), port.id, @@ -1237,6 +1280,7 @@ admin_state_up=port.admin_state_up, device_id=port.device_id, device_owner=port.device_owner, + binding__host_id=port.binding__host_id, **extension_kwargs)\ .AndRaise(self.exceptions.neutron) self.mox.ReplayAll() @@ -1246,7 +1290,10 @@ 'name': port.name, 'admin_state': port.admin_state_up, 'device_id': port.device_id, - 'device_owner': port.device_owner} + 'device_owner': port.device_owner, + 'binding__host_id': port.binding__host_id} + if binding: + form_data['binding__vnic_type'] = port.binding__vnic_type if mac_learning: form_data['mac_state'] = True url = reverse('horizon:admin:networks:editport', diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/networks/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/networks/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -39,6 +39,7 @@ class IndexView(tables.DataTableView): table_class = networks_tables.NetworksTable template_name = 'admin/networks/index.html' + page_title = _("Networks") @memoized.memoized_method def _get_tenant_list(self): @@ -46,7 +47,8 @@ tenants, has_more = api.keystone.tenant_list(self.request) except Exception: tenants = [] - msg = _('Unable to retrieve instance project information.') + msg = _("Unable to retrieve information about the " + "networks' projects.") exceptions.handle(self.request, msg) tenant_dict = SortedDict([(t.id, t) for t in tenants]) @@ -94,6 +96,7 @@ form_class = project_forms.CreateNetwork template_name = 'admin/networks/create.html' success_url = reverse_lazy('horizon:admin:networks:index') + page_title = _("Create Network") class DetailView(tables.MultiTableView): @@ -101,6 +104,7 @@ ports_tables.PortsTable, agents_tables.DHCPAgentsTable) template_name = 'project/networks/detail.html' + page_title = _("Network Details: {{ network.name }}") def get_subnets_data(self): try: @@ -175,6 +179,7 @@ form_class = project_forms.UpdateNetwork template_name = 'admin/networks/update.html' success_url = reverse_lazy('horizon:admin:networks:index') + submit_url = "horizon:admin:networks:update" def get_initial(self): network = self._get_object() diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Usage Overview" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Overview") %} -{% endblock page_header %} - {% block main %} {% if monitoring %}
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/overview/tests.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/overview/tests.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/overview/tests.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/overview/tests.py 2015-03-19 19:07:23.000000000 +0000 @@ -115,10 +115,10 @@ %.2f ''' % (usage_list[0].vcpus, - sizeformat.diskgbformat(usage_list[0].disk_gb_hours), + sizeformat.diskgbformat(usage_list[0].local_gb), sizeformat.mb_float_format(usage_list[0].memory_mb), usage_list[0].vcpu_hours, - usage_list[0].total_local_gb_usage, + usage_list[0].disk_gb_hours, usage_list[0].memory_mb_hours) ) @@ -134,10 +134,10 @@ %.2f ''' % (usage_list[1].vcpus, - sizeformat.diskgbformat(usage_list[1].disk_gb_hours), + sizeformat.diskgbformat(usage_list[1].local_gb), sizeformat.mb_float_format(usage_list[1].memory_mb), usage_list[1].vcpu_hours, - usage_list[1].total_local_gb_usage, + usage_list[1].disk_gb_hours, usage_list[1].memory_mb_hours) ) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/ports/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/ports/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/ports/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/ports/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -12,37 +12,17 @@ # License for the specific language governing permissions and limitations # under the License. -from django.utils.translation import pgettext_lazy from django.utils.translation import ugettext_lazy as _ from horizon import tables -from openstack_dashboard.dashboards.project.networks.ports \ - import tables as networks_tables from openstack_dashboard.dashboards.project.routers.ports \ import tables as routers_tables -DISPLAY_CHOICES = ( - ("UP", pgettext_lazy("Admin state of a Network", u"UP")), - ("DOWN", pgettext_lazy("Admin state of a Network", u"DOWN")), -) - - -class PortsTable(tables.DataTable): - name = tables.Column("name", +class PortsTable(routers_tables.PortsTable): + name = tables.Column("name_or_id", verbose_name=_("Name"), link="horizon:admin:networks:ports:detail") - fixed_ips = tables.Column(networks_tables.get_fixed_ips, - verbose_name=_("Fixed IPs")) - status = tables.Column("status", verbose_name=_("Status")) - device_owner = tables.Column(routers_tables.get_device_owner, - verbose_name=_("Type")) - admin_state = tables.Column("admin_state", - verbose_name=_("Admin State"), - display_choices=DISPLAY_CHOICES) - - def get_object_display(self, port): - return port.id class Meta(object): name = "interfaces" diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/templates/routers/detail.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/templates/routers/detail.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/templates/routers/detail.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/templates/routers/detail.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Router Details" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Router Details") %} -{% endblock page_header %} - {% block main %}
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/templates/routers/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/templates/routers/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/templates/routers/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/templates/routers/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Routers" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Routers") %} -{% endblock page_header %} - {% block main %} {{ table.render }} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/templates/routers/_update.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/templates/routers/_update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/templates/routers/_update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/templates/routers/_update.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -{% extends "horizon/common/_modal_form.html" %} -{% load i18n %} -{% load url from future %} - -{% block form_id %}update_router_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:routers:update' router_id %}{% endblock %} - -{% block modal-header %}{% trans "Edit Router" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

{% trans "You may update the editable properties of your router here." %}

-
-{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} -{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/templates/routers/update.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/templates/routers/update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/templates/routers/update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/templates/routers/update.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Update Router" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Router") %} -{% endblock page_header %} - {% block main %} {% include 'admin/routers/_update.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/routers/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/routers/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -66,5 +66,6 @@ class UpdateView(r_views.UpdateView): form_class = rforms.UpdateForm - template_name = 'admin/routers/update.html' + template_name = 'project/routers/update.html' success_url = reverse_lazy("horizon:admin:routers:index") + submit_url = "horizon:admin:routers:update" diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/snapshots/forms.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/snapshots/forms.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/snapshots/forms.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/snapshots/forms.py 2015-03-19 19:07:23.000000000 +0000 @@ -11,6 +11,7 @@ # under the License. +from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ from horizon import exceptions @@ -44,6 +45,7 @@ ' status: "%s".') % choice) return True except Exception: + redirect = reverse("horizon:admin:volumes:index") exceptions.handle(request, - _('Unable to update volume snapshot status.')) - return False + _('Unable to update volume snapshot status.'), + redirect=redirect) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/snapshots/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/snapshots/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/snapshots/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/snapshots/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -30,8 +30,13 @@ class UpdateStatusView(forms.ModalFormView): form_class = vol_snapshot_forms.UpdateStatus + modal_header = _("Update Volume Snapshot Status") + modal_id = "update_volume_snapshot_status" template_name = 'admin/volumes/snapshots/update_status.html' + submit_label = _("Update Status") + submit_url = "horizon:admin:volumes:snapshots:update_status" success_url = reverse_lazy("horizon:admin:volumes:snapshots_tab") + page_title = _("Update Volume Snapshot Status") @memoized.memoized_method def get_object(self): @@ -48,6 +53,8 @@ def get_context_data(self, **kwargs): context = super(UpdateStatusView, self).get_context_data(**kwargs) context['snapshot_id'] = self.kwargs["snapshot_id"] + args = (self.kwargs['snapshot_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) return context def get_initial(self): diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,14 +2,10 @@ {% load i18n %} {% block title %}{% trans "Volumes" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Volumes") %} -{% endblock page_header %} - {% block main %}
{{ tab_group.render }}
-{% endblock %} \ No newline at end of file +{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/snapshots/_update_status.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/snapshots/_update_status.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/snapshots/_update_status.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/snapshots/_update_status.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,30 +1,11 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}{% endblock %} -{% block form_action %}{% url 'horizon:admin:volumes:snapshots:update_status' snapshot_id %}{% endblock %} - -{% block modal_id %}update_volume_snapshot_status_modal{% endblock %} -{% block modal-header %}{% trans "Update Volume Snapshot Status" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
+{% block modal-body-right %}

{% trans "Description:" %}

{% blocktrans %} The status of a volume snapshot is normally managed automatically. In some circumstances an administrator may need to explicitly update the status value. This is equivalent to the cinder snapshot-reset-state command. {% endblocktrans %}

-
{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} -{% endblock %} \ No newline at end of file diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/snapshots/update_status.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/snapshots/update_status.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/snapshots/update_status.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/snapshots/update_status.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Update Volume Snapshot Status" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Volume Snapshot Status") %} -{% endblock page_header %} - {% block main %} {% include 'admin/volumes/snapshots/_update_status.html' %} -{% endblock %} \ No newline at end of file +{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/detail.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/detail.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/detail.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/detail.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Volume Details" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=page_title %} -{% endblock page_header %} - {% block main %}
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_update_status.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_update_status.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_update_status.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/_update_status.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,30 +1,11 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}{% endblock %} -{% block form_action %} {% url 'horizon:admin:volumes:volumes:update_status' volume_id %}{% endblock %} - -{% block modal_id %}update_volume_status_modal{% endblock %} -{% block modal-header %}{% trans "Update Volume Status" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
+{% block modal-body-right %}

{% trans "Description:" %}

{% blocktrans %} The status of a volume is normally managed automatically. In some circumstances an administrator may need to explicitly update the status value. This is equivalent to the cinder reset-state command. {% endblocktrans %}

-
-{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/update_status.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/update_status.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/update_status.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volumes/update_status.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Update Volume Status" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Volume Status") %} -{% endblock page_header %} - {% block main %} {% include 'admin/volumes/volumes/_update_status.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_associate_qos_spec.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_associate_qos_spec.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_associate_qos_spec.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_associate_qos_spec.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,36 +1,9 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}{% endblock %} -{% block form_action %}{% url 'horizon:admin:volumes:volume_types:manage_qos_spec_association' type_id %}{% endblock %} - -{% block modal_id %}associate_qos_spec_modal{% endblock %} -{% block modal-header %}{% trans "Associate QoS Spec with Volume Type"%}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
+{% block modal-body-right %}

{% trans "Description:" %}

-

{% blocktrans %} - Add, modify or remove the QoS Spec associated with this volume type. -
-
- "None" indicates that no QoS Spec is currently associated. Conversely, setting - the QoS Spec to "None" will remove the current association. -
-
- This is equivalent to the cinder qos-associate and cinder qos-disassociate - commands. - {% endblocktrans %}

-
+

{% trans "Add, modify or remove the QoS Spec associated with this volume type." %}

+

{% blocktrans %}"None" indicates that no QoS Spec is currently associated. Conversely, setting the QoS Spec to "None" will remove the current association.{% endblocktrans %}

+

{% blocktrans %}This is equivalent to the cinder qos-associate and cinder qos-disassociate commands.{% endblocktrans %}

{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} -{% endblock %} \ No newline at end of file diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/associate_qos_spec.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/associate_qos_spec.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/associate_qos_spec.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/associate_qos_spec.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Associate QoS Spec" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Associate QoS Spec with Volume Type") %} -{% endblock page_header %} - {% block main %} {% include 'admin/volumes/volume_types/qos_specs/_associate_qos_spec.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_qos_spec.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_qos_spec.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_qos_spec.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_qos_spec.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,20 +1,7 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}{% endblock %} -{% block form_action %}{% url 'horizon:admin:volumes:volume_types:create_qos_spec' %}{% endblock %} - -{% block modal_id %}create_volume_type_modal{% endblock %} -{% block modal-header %}{% trans "Create QoS Spec" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
+{% block modal-body-right %}

{% trans "Description:" %}

{% blocktrans %} QoS Specs can be associated with volume types. @@ -27,12 +14,5 @@ Each QoS Specs entity will have a "Consumer" value which indicates where the administrator would like the QoS policy to be enforced. This value can be "front-end" (Nova Compute), "back-end" (Cinder back-end), or "both". - {% endblocktrans %} -

-
-{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} + {% endblocktrans %}

{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_qos_spec.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_qos_spec.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_qos_spec.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_qos_spec.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create QoS Spec" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create a QoS Spec") %} -{% endblock page_header %} - {% block main %} {% include 'admin/volumes/volume_types/_create_qos_spec.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_volume_type_encryption.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_volume_type_encryption.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_volume_type_encryption.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_volume_type_encryption.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,20 +1,7 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}create_volume_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:volumes:volume_types:create_type_encryption' volume_type_id %}{% endblock %} - -{% block modal_id %}create_volume_type_modal{% endblock %} -{% block modal-header %}{% trans "Create Volume Type Encryption" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
+{% block modal-body-right %}

{% trans "Description:" %}

{% trans "Creating encryption for a volume type causes all volumes with that volume type to be encrypted. Encryption information cannot be added to a volume type if volumes are currently in use with that volume type." %} @@ -31,10 +18,4 @@

{% blocktrans %}The Key Size is the size of the encryption key, in bits (e.g., 128, 256). If the field is left empty, the provider default will be used.{% endblocktrans %}

-
-{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_volume_type_encryption.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_volume_type_encryption.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_volume_type_encryption.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_volume_type_encryption.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create Encrypted Volume Type" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create an Encrypted Volume Type") %} -{% endblock page_header %} - {% block main %} {% include 'admin/volumes/volume_types/_create_volume_type_encryption.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_volume_type.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_volume_type.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_volume_type.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_create_volume_type.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,20 +1,7 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}{% endblock %} -{% block form_action %}{% url 'horizon:admin:volumes:volume_types:create_type' %}{% endblock %} - -{% block modal_id %}create_volume_type_modal{% endblock %} -{% block modal-header %}{% trans "Create Volume Type" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
+{% block modal-body-right %}

{% trans "Description:" %}

{% blocktrans %} Volume type is a type or label that can be selected at volume creation @@ -26,10 +13,4 @@ pair(s) for that volume type. {% endblocktrans %}

-
-{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_volume_type.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_volume_type.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_volume_type.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/create_volume_type.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create Volume Type" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create a Volume Type") %} -{% endblock page_header %} - {% block main %} {% include 'admin/volumes/volume_types/_create_volume_type.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_edit_qos_spec_consumer.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_edit_qos_spec_consumer.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_edit_qos_spec_consumer.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/_edit_qos_spec_consumer.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,30 +1,11 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}{% endblock %} -{% block form_action %}{% url 'horizon:admin:volumes:volume_types:edit_qos_spec_consumer' qos_spec_id %}{% endblock %} - -{% block modal_id %}associate_qos_spec_modal{% endblock %} -{% block modal-header %}{% trans "Edit Consumer of QoS Spec" %} {% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
+{% block modal-body-right %}

{% trans "Description:" %}

{% blocktrans %} Each QoS Specs entity will have a "Consumer" value which indicates where the administrator would like the QoS policy to be enforced. This value can be "front-end" (Nova Compute), "back-end" (Cinder back-end), or "both". {% endblocktrans %}

-
{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} -{% endblock %} \ No newline at end of file diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/edit_qos_spec_consumer.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/edit_qos_spec_consumer.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/edit_qos_spec_consumer.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/edit_qos_spec_consumer.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Edit QoS Spec Consumer" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Edit QoS Spec Consumer") %} -{% endblock page_header %} - {% block main %} {% include 'admin/volumes/volume_types/_edit_qos_spec_consumer.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/_create.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/_create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/_create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/_create.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,28 +1,7 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}extra_spec_create_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:volumes:volume_types:extras:create' vol_type.id %}{% endblock %} - - -{% block modal_id %}extra_spec_create_modal{% endblock %} -{% block modal-header %}{% trans "Create Volume Type Extra Spec" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

{% trans 'Create a new "extra spec" key-value pair for a volume type.' %}

-
+{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans 'Create a new "extra spec" key-value pair for a volume type.' %}

{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} -{% endblock %} - diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/_edit.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/_edit.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/_edit.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/extras/_edit.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,28 +1,7 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}extra_spec_edit_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:volumes:volume_types:extras:edit' vol_type.id key %}{% endblock %} - - -{% block modal_id %}extra_spec_edit_modal{% endblock %} -{% block modal-header %}{% blocktrans %}Edit Extra Spec Value: {{ key }}{% endblocktrans %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

{% blocktrans with key=key %}Update the "extra spec" value for "{{ key }}"{% endblocktrans %}

-
+{% block modal-body-right %} +

{% trans "Description:" %}

+

{% blocktrans with key=key %}Update the "extra spec" value for "{{ key }}"{% endblocktrans %}

{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} -{% endblock %} - diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/_create.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/_create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/_create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/_create.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,28 +1,7 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}extra_spec_create_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:volumes:volume_types:qos_specs:create' qos_spec_id %}{% endblock %} - - -{% block modal_id %}qos_spec_create_modal{% endblock %} -{% block modal-header %}{% trans "Create Spec" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

{% blocktrans with key=qos_spec_name %}Create a new "spec" key-value pair for QoS Spec "{{ qos_spec_name }}"{% endblocktrans %}

-
+{% block modal-body-right %} +

{% trans "Description:" %}

+

{% blocktrans with key=qos_spec_name %}Create a new "spec" key-value pair for QoS Spec "{{ qos_spec_name }}"{% endblocktrans %}

{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} -{% endblock %} - diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/_edit.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/_edit.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/_edit.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/qos_specs/_edit.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,27 +1,7 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}qos_spec_edit_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:volumes:volume_types:qos_specs:edit' qos_spec_id key %}{% endblock %} - - -{% block modal_id %}qos_spec_edit_modal{% endblock %} -{% block modal-header %}{% trans "Edit Spec Value: " %} {{ key }}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

{% blocktrans with key=key %}Update the spec value for "{{ key }}"{% endblocktrans %}

-
-{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% blocktrans with key=key %}Update the spec value for "{{ key }}"{% endblocktrans %}

{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/volume_encryption_type_detail.html horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/volume_encryption_type_detail.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/volume_encryption_type_detail.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/templates/volumes/volume_types/volume_encryption_type_detail.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Volume Type Encryption Details" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Volume Type Encryption Details") %} -{% endblock page_header %} - {% block main %}
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/tests.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/tests.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/tests.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/tests.py 2015-03-19 19:07:23.000000000 +0000 @@ -25,11 +25,14 @@ class VolumeTests(test.BaseAdminViewTests): @test.create_stubs({api.nova: ('server_list',), - cinder: ('volume_list',), + cinder: ('volume_list', + 'volume_snapshot_list'), keystone: ('tenant_list',)}) def test_index(self): cinder.volume_list(IsA(http.HttpRequest), search_opts={ 'all_tenants': True}).AndReturn(self.cinder_volumes.list()) + cinder.volume_snapshot_list(IsA(http.HttpRequest), search_opts={ + 'all_tenants': True}).AndReturn([]) api.nova.server_list(IsA(http.HttpRequest), search_opts={ 'all_tenants': True}) \ .AndReturn([self.servers.list(), False]) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -15,6 +15,7 @@ """ Admin views for managing volumes and snapshots. """ +from django.utils.translation import ugettext_lazy as _ from horizon import tabs @@ -25,3 +26,4 @@ class IndexView(tabs.TabbedTableView): tab_group_class = volumes_tabs.VolumesGroupTabs template_name = 'admin/volumes/index.html' + page_title = _("Volumes") diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volumes/forms.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volumes/forms.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volumes/forms.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volumes/forms.py 2015-03-19 19:07:23.000000000 +0000 @@ -16,6 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.core.urlresolvers import reverse from django.forms import ValidationError # noqa from django.utils.translation import ugettext_lazy as _ @@ -128,8 +129,9 @@ % volume_name) return True except Exception: - exceptions.handle(request, _("Unable to manage volume.")) - return False + redirect = reverse("horizon:admin:volumes:index") + exceptions.handle(request, _("Unable to manage volume."), + redirect=redirect) class UnmanageVolume(forms.SelfHandlingForm): @@ -158,8 +160,9 @@ % data['name']) return True except Exception: - exceptions.handle(request, _("Unable to unmanage volume.")) - return False + redirect = reverse("horizon:admin:volumes:index") + exceptions.handle(request, _("Unable to unmanage volume."), + redirect=redirect) class CreateVolumeType(forms.SelfHandlingForm): @@ -174,9 +177,10 @@ % data['name']) return volume_type except Exception: + redirect = reverse("horizon:admin:volumes:index") exceptions.handle(request, - _('Unable to create volume type.')) - return False + _('Unable to create volume type.'), + redirect=redirect) class UpdateStatus(forms.SelfHandlingForm): @@ -215,10 +219,10 @@ new_status) return True except Exception: + redirect = reverse("horizon:admin:volumes:index") exceptions.handle(request, _('Unable to update volume status to "%s".') % - new_status) - return False + new_status, redirect=redirect) class CreateQosSpec(forms.SelfHandlingForm): @@ -236,6 +240,7 @@ % data['name']) return qos_spec except Exception: + redirect = reverse("horizon:admin:volumes:index") exceptions.handle(request, - _('Unable to create QoS Spec.')) - return False + _('Unable to create QoS Spec.'), + redirect=redirect) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volumes/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volumes/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volumes/views.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volumes/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -28,14 +28,6 @@ class DetailView(volumes_views.DetailView): template_name = "admin/volumes/volumes/detail.html" - def get_context_data(self, **kwargs): - context = super(DetailView, self).get_context_data(**kwargs) - volume = context["volume"] - context["page_title"] = _("Volume Details: " - "%(volume_name)s") % {'volume_name': - volume.name} - return context - def get_redirect_url(self): return reverse('horizon:admin:volumes:index') @@ -100,12 +92,19 @@ class UpdateStatusView(forms.ModalFormView): form_class = volumes_forms.UpdateStatus + modal_header = _("Update Volume Status") + modal_id = "update_volume_status_modal" template_name = 'admin/volumes/volumes/update_status.html' + submit_label = _("Update Status") + submit_url = "horizon:admin:volumes:volumes:update_status" success_url = reverse_lazy('horizon:admin:volumes:index') + page_title = _("Update Volume Status") def get_context_data(self, **kwargs): context = super(UpdateStatusView, self).get_context_data(**kwargs) context["volume_id"] = self.kwargs['volume_id'] + args = (self.kwargs['volume_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) return context @memoized.memoized_method diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/extras/forms.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/extras/forms.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/extras/forms.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/extras/forms.py 2015-03-19 19:07:23.000000000 +0000 @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ from openstack_dashboard import api @@ -33,8 +34,10 @@ messages.success(request, msg) return True except Exception: + redirect = reverse("horizon:admin:volumes:index") exceptions.handle(request, - _("Unable to create volume type extra spec.")) + _("Unable to create volume type extra spec."), + redirect=redirect) class EditExtraSpec(forms.SelfHandlingForm): @@ -51,5 +54,7 @@ messages.success(request, msg) return True except Exception: + redirect = reverse("horizon:admin:volumes:index") exceptions.handle(request, - _("Unable to edit volume type extra spec.")) + _("Unable to edit volume type extra spec."), + redirect=redirect) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/extras/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/extras/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/extras/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/extras/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -58,18 +58,35 @@ class CreateView(ExtraSpecMixin, forms.ModalFormView): form_class = project_forms.CreateExtraSpec + form_id = "extra_spec_create_form" + modal_header = _("Create Volume Type Extra Spec") + modal_id = "extra_spec_create_modal" + submit_label = _("Create") + submit_url = "horizon:admin:volumes:volume_types:extras:create" template_name = 'admin/volumes/volume_types/extras/create.html' + success_url = 'horizon:admin:volumes:volume_types:extras:index' def get_initial(self): return {'type_id': self.kwargs['type_id']} def get_success_url(self): - return ("/admin/volumes/volume_types/%s/extras/" % - (self.kwargs['type_id'])) + return reverse(self.success_url, + args=(self.kwargs['type_id'],)) + + def get_context_data(self, **kwargs): + context = super(CreateView, self).get_context_data(**kwargs) + args = (self.kwargs['type_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) + return context class EditView(ExtraSpecMixin, forms.ModalFormView): form_class = project_forms.EditExtraSpec + form_id = "extra_spec_edit_form" + modal_header = _('Edit Extra Spec Value: %s') + modal_id = "extra_spec_edit_modal" + submit_label = _("Save") + submit_url = "horizon:admin:volumes:volume_types:extras:edit" template_name = 'admin/volumes/volume_types/extras/edit.html' success_url = 'horizon:admin:volumes:volume_types:extras:index' @@ -92,3 +109,10 @@ return {'type_id': type_id, 'key': key, 'value': extra_specs.get(key, '')} + + def get_context_data(self, **kwargs): + context = super(EditView, self).get_context_data(**kwargs) + args = (self.kwargs['type_id'], self.kwargs['key'],) + context['submit_url'] = reverse(self.submit_url, args=args) + context['modal_header'] = self.modal_header % self.kwargs['key'] + return context diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/forms.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/forms.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/forms.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/forms.py 2015-03-19 19:07:23.000000000 +0000 @@ -11,6 +11,7 @@ # under the License. +from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ from horizon import exceptions @@ -52,9 +53,10 @@ 'volume type: %s') % data['name']) return volume_type except Exception: + redirect = reverse("horizon:admin:volumes:index") exceptions.handle(request, - _('Unable to create encrypted volume type.')) - return False + _('Unable to create encrypted volume type.'), + redirect=redirect) class ManageQosSpecAssociation(forms.SelfHandlingForm): @@ -140,9 +142,10 @@ _('Successfully updated QoS Spec association.')) return True except Exception: + redirect = reverse("horizon:admin:volumes:index") exceptions.handle(request, - _('Error updating QoS Spec association.')) - return False + _('Error updating QoS Spec association.'), + redirect=redirect) class EditQosSpecConsumer(forms.SelfHandlingForm): @@ -182,5 +185,6 @@ _('Successfully modified QoS Spec consumer.')) return True except Exception: - exceptions.handle(request, _('Error editing QoS Spec consumer.')) - return False + redirect = reverse("horizon:admin:volumes:index") + exceptions.handle(request, _('Error editing QoS Spec consumer.'), + redirect=redirect) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/forms.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/forms.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/forms.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/forms.py 2015-03-19 19:07:23.000000000 +0000 @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ from horizon import exceptions @@ -38,9 +39,10 @@ messages.success(request, msg) return True except Exception: + redirect = reverse("horizon:admin:volumes:index") exceptions.handle(request, - _("Unable to create spec.")) - return False + _("Unable to create spec."), + redirect=redirect) class EditKeyValuePair(forms.SelfHandlingForm): @@ -65,6 +67,7 @@ messages.success(request, msg) return True except Exception: + redirect = reverse("horizon:admin:volumes:index") exceptions.handle(request, - _("Unable to edit spec.")) - return False + _("Unable to edit spec."), + redirect=redirect) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/tests.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/tests.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/tests.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/tests.py 2015-03-19 19:07:23.000000000 +0000 @@ -65,6 +65,7 @@ self.assertMessageCount(success=1) @test.create_stubs({api.cinder: ('volume_type_list_with_qos_associations', + 'volume_encryption_type_list', 'qos_spec_list', 'qos_spec_delete',)}) def test_delete_qos_spec(self): @@ -74,6 +75,8 @@ api.cinder.volume_type_list_with_qos_associations( IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) + api.cinder.volume_encryption_type_list(IsA(http.HttpRequest))\ + .AndReturn(self.cinder_volume_encryption_types.list()[0:1]) api.cinder.qos_spec_list(IsA(http.HttpRequest)).\ AndReturn(self.cinder_qos_specs.list()) api.cinder.qos_spec_delete(IsA(http.HttpRequest), diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/qos_specs/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -68,7 +68,12 @@ class CreateKeyValuePairView(QosSpecMixin, forms.ModalFormView): # this for creating a spec key-value pair for an existing QOS Spec form_class = project_forms.CreateKeyValuePair + form_id = "extra_spec_create_form" + modal_header = _("Create Spec") + modal_id = "qos_spec_create_modal" template_name = 'admin/volumes/volume_types/qos_specs/create.html' + submit_label = _("Create") + submit_url = "horizon:admin:volumes:volume_types:qos_specs:create" success_url = 'horizon:admin:volumes:volume_types:qos_specs:index' def get_initial(self): @@ -79,10 +84,22 @@ return reverse(self.success_url, args=(self.kwargs['qos_spec_id'],)) + def get_context_data(self, **kwargs): + context = super(CreateKeyValuePairView, self).\ + get_context_data(**kwargs) + args = (self.kwargs['qos_spec_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) + return context + class EditKeyValuePairView(QosSpecMixin, forms.ModalFormView): form_class = project_forms.EditKeyValuePair + form_id = "qos_spec_edit_form" + modal_header = _("Edit Spec Value") + modal_id = "qos_spec_edit_modal" template_name = 'admin/volumes/volume_types/qos_specs/edit.html' + submit_label = _("Save") + submit_url = "horizon:admin:volumes:volume_types:qos_specs_edit" success_url = 'horizon:admin:volumes:volume_types:qos_specs:index' def get_success_url(self): @@ -102,3 +119,9 @@ return {'qos_spec_id': qos_spec_id, 'key': key, 'value': qos_specs.specs.get(key, '')} + + def get_context_data(self, **kwargs): + context = super(EditKeyValuePairView, self).get_context_data(**kwargs) + args = (self.kwargs['qos_spec_id'], self.kwargs['key'],) + context['submit_url'] = reverse(self.submit_url, args=args) + return context diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/admin/volumes/volume_types/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/admin/volumes/volume_types/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -32,8 +32,13 @@ class CreateVolumeTypeView(forms.ModalFormView): form_class = volumes_forms.CreateVolumeType + modal_header = _("Create Volume Type") + modal_id = "create_volume_type_modal" template_name = 'admin/volumes/volume_types/create_volume_type.html' + submit_label = _("Create Volume Type") + submit_url = reverse_lazy("horizon:admin:volumes:volume_types:create_type") success_url = 'horizon:admin:volumes:volume_types_tab' + page_title = _("Create a Volume Type") def get_success_url(self): return reverse(self.success_url) @@ -42,6 +47,7 @@ class VolumeTypeEncryptionDetailView(generic.TemplateView): template_name = ("admin/volumes/volume_types" "/volume_encryption_type_detail.html") + page_title = _("Volume Type Encryption Details") def get_context_data(self, **kwargs): context = super(VolumeTypeEncryptionDetailView, self).\ @@ -73,9 +79,15 @@ class CreateVolumeTypeEncryptionView(forms.ModalFormView): form_class = volume_types_forms.CreateVolumeTypeEncryption + form_id = "create_volume_form" + modal_header = _("Create Volume Type Encryption") + modal_id = "create_volume_type_modal" template_name = ("admin/volumes/volume_types/" "create_volume_type_encryption.html") + submit_label = _("Create Volume Type Encryption") + submit_url = "horizon:admin:volumes:volume_types:create_type_encryption" success_url = reverse_lazy('horizon:admin:volumes:index') + page_title = _("Create an Encrypted Volume Type") @memoized.memoized_method def get_name(self): @@ -94,6 +106,8 @@ context = super(CreateVolumeTypeEncryptionView, self).\ get_context_data(**kwargs) context['volume_type_id'] = self.kwargs['volume_type_id'] + args = (self.kwargs['volume_type_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) return context def get_initial(self): @@ -104,8 +118,14 @@ class CreateQosSpecView(forms.ModalFormView): form_class = volumes_forms.CreateQosSpec + modal_header = _("Create QoS Spec") + modal_id = "create_volume_type_modal" template_name = 'admin/volumes/volume_types/create_qos_spec.html' success_url = 'horizon:admin:volumes:volume_types_tab' + page_title = _("Create a QoS Spec") + submit_label = _("Create") + submit_url = reverse_lazy( + "horizon:admin:volumes:volume_types:create_qos_spec") def get_success_url(self): return reverse(self.success_url) @@ -113,8 +133,13 @@ class EditQosSpecConsumerView(forms.ModalFormView): form_class = volume_types_forms.EditQosSpecConsumer + modal_header = _("Edit Consumer of QoS Spec") + modal_id = "edit_qos_spec_modal" template_name = 'admin/volumes/volume_types/edit_qos_spec_consumer.html' + submit_label = _("Modify Consumer") + submit_url = "horizon:admin:volumes:volume_types:edit_qos_spec_consumer" success_url = 'horizon:admin:volumes:volume_types_tab' + page_title = _("Edit QoS Spec Consumer") def get_success_url(self): return reverse(self.success_url) @@ -123,6 +148,8 @@ context = super(EditQosSpecConsumerView, self).\ get_context_data(**kwargs) context['qos_spec_id'] = self.kwargs["qos_spec_id"] + args = (self.kwargs['qos_spec_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) return context @memoized.memoized_method @@ -145,8 +172,14 @@ class ManageQosSpecAssociationView(forms.ModalFormView): form_class = volume_types_forms.ManageQosSpecAssociation + modal_header = _("Associate QoS Spec with Volume Type") + modal_id = "associate_qos_spec_modal" template_name = 'admin/volumes/volume_types/associate_qos_spec.html' + submit_label = _("Associate") + submit_url = "horizon:admin:volumes:volume_types:"\ + "manage_qos_spec_association" success_url = 'horizon:admin:volumes:volume_types_tab' + page_title = _("Associate QoS Spec with Volume Type") def get_success_url(self): return reverse(self.success_url) @@ -155,6 +188,8 @@ context = super(ManageQosSpecAssociationView, self).\ get_context_data(**kwargs) context['type_id'] = self.kwargs["type_id"] + args = (self.kwargs['type_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) return context @memoized.memoized_method diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/domains/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/domains/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/domains/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/domains/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -16,6 +16,7 @@ from django.conf import settings from django.core.urlresolvers import reverse +from django.template import defaultfilters as filters from django.utils.http import urlencode from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy @@ -199,7 +200,8 @@ description = tables.Column(lambda obj: getattr(obj, 'description', None), verbose_name=_('Description')) id = tables.Column('id', verbose_name=_('Domain ID')) - enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True) + enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True, + filters=(filters.yesno, filters.capfirst)) class Meta(object): name = "domains" diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/domains/templates/domains/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/domains/templates/domains/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/domains/templates/domains/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/domains/templates/domains/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -3,7 +3,7 @@ {% block title %}{% trans "Domains" %}{% endblock %} {% block page_header %} - {% include "horizon/common/_domain_page_header.html" with title=_("Domains") %} + {% include "horizon/common/_domain_page_header.html" with title=page_title %} {% endblock page_header %} {% block main %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/domains/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/domains/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/domains/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/domains/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -33,6 +33,7 @@ class IndexView(tables.DataTableView): table_class = project_tables.DomainsTable template_name = constants.DOMAINS_INDEX_VIEW_TEMPLATE + page_title = _("Domains") def get_data(self): domains = [] diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -195,7 +195,9 @@ enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True, status_choices=STATUS_CHOICES, - empty_value="False") + filters=(defaultfilters.yesno, + defaultfilters.capfirst), + empty_value=_('False')) class GroupMembersTable(UsersTable): diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/templates/groups/create.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/templates/groups/create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/templates/groups/create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/templates/groups/create.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create Group" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create Group") %} -{% endblock page_header %} - {% block main %} {% include 'identity/groups/_create.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/templates/groups/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/templates/groups/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/templates/groups/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/templates/groups/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -3,7 +3,7 @@ {% block title %}{% trans "Groups" %}{% endblock %} {% block page_header %} - {% include "horizon/common/_domain_page_header.html" with title=_("Groups") %} + {% include "horizon/common/_domain_page_header.html" with title=page_title %} {% endblock page_header %} {% block main %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/templates/groups/manage.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/templates/groups/manage.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/templates/groups/manage.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/templates/groups/manage.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans 'Group Management' %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=page_title %} -{% endblock page_header %} - {% block main %} {{ group_members_table.render }} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/templates/groups/update.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/templates/groups/update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/templates/groups/update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/templates/groups/update.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Update Group" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Group") %} -{% endblock page_header %} - {% block main %} {% include 'identity/groups/_update.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/groups/views.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/groups/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -35,6 +35,7 @@ class IndexView(tables.DataTableView): table_class = project_tables.GroupsTable template_name = constants.GROUPS_INDEX_VIEW_TEMPLATE + page_title = _("Groups") def get_data(self): groups = [] @@ -61,6 +62,7 @@ submit_url = "horizon:identity:groups:create" submit_label = _("Create Group") success_url = reverse_lazy(constants.GROUPS_INDEX_URL) + page_title = _("Create Group") class UpdateView(forms.ModalFormView): @@ -71,6 +73,7 @@ submit_url = "horizon:identity:groups:update" submit_label = _("Update Group") success_url = reverse_lazy(constants.GROUPS_INDEX_URL) + page_title = _("Update Group") @memoized.memoized_method def get_object(self): @@ -120,14 +123,11 @@ class ManageMembersView(GroupManageMixin, tables.DataTableView): table_class = project_tables.GroupMembersTable template_name = constants.GROUPS_MANAGE_VIEW_TEMPLATE + page_title = _("Group Management: {{ group.name }}") def get_context_data(self, **kwargs): context = super(ManageMembersView, self).get_context_data(**kwargs) - group = self._get_group() - context['group'] = group - context['page_title'] = _("Group Management: " - "%(group_name)s") % {'group_name': - group.name} + context['group'] = self._get_group() return context def get_data(self): diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/templates/projects/_common_horizontal_form.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/templates/projects/_common_horizontal_form.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/templates/projects/_common_horizontal_form.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/templates/projects/_common_horizontal_form.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,3 @@ +
+ {% include "horizon/common/_horizontal_fields.html" %} +
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/templates/projects/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/templates/projects/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/templates/projects/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/templates/projects/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -3,7 +3,7 @@ {% block title %}{% trans "Projects" %}{% endblock %} {% block page_header %} - {% include "horizon/common/_domain_page_header.html" with title=_("Projects") %} + {% include "horizon/common/_domain_page_header.html" with title=page_title %} {% endblock page_header %} {% block main %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/templates/projects/usage.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/templates/projects/usage.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/templates/projects/usage.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/templates/projects/usage.html 2015-03-19 19:07:23.000000000 +0000 @@ -3,7 +3,7 @@ {% block title %}{% trans "Project Usage Overview" %}{% endblock %} {% block page_header %} - {% include "horizon/common/_domain_page_header.html" with title=_("Project Usage") %} + {% include "horizon/common/_domain_page_header.html" with title=page_title %} {% endblock %} {% block main %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/tests.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/tests.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/tests.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/tests.py 2015-03-19 19:07:29.000000000 +0000 @@ -20,6 +20,7 @@ import django from django.core.urlresolvers import reverse from django import http +from django.test.utils import override_settings from django.utils import timezone from django.utils import unittest @@ -31,6 +32,7 @@ from openstack_dashboard import api from openstack_dashboard.dashboards.identity.projects import workflows +from openstack_dashboard import policy_backend from openstack_dashboard.test import helpers as test from openstack_dashboard import usage from openstack_dashboard.usage import quotas @@ -84,6 +86,7 @@ class ProjectsViewNonAdminTests(test.TestCase): + @override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @test.create_stubs({api.keystone: ('tenant_list',)}) def test_index(self): api.keystone.tenant_list(IsA(http.HttpRequest), @@ -1536,41 +1539,46 @@ self.assertMessageCount(error=2, warning=0) self.assertRedirectsNoFollow(res, INDEX_URL) - @test.create_stubs({api.keystone: ('get_default_role', - 'tenant_get', - 'domain_get'), - quotas: ('get_tenant_quota_data', - 'get_disabled_quotas')}) - def test_update_project_when_default_role_does_not_exist(self): - project = self.tenants.first() - domain_id = project.domain_id - quota = self.quotas.first() - - api.keystone.get_default_role(IsA(http.HttpRequest)) \ - .MultipleTimes().AndReturn(None) # Default role doesn't exist - api.keystone.tenant_get(IsA(http.HttpRequest), self.tenant.id, - admin=True) \ - .AndReturn(project) - api.keystone.domain_get(IsA(http.HttpRequest), domain_id) \ - .AndReturn(self.domain) - quotas.get_disabled_quotas(IsA(http.HttpRequest)) \ - .AndReturn(self.disabled_quotas.first()) - quotas.get_tenant_quota_data(IsA(http.HttpRequest), - tenant_id=self.tenant.id) \ - .AndReturn(quota) - self.mox.ReplayAll() - - url = reverse('horizon:identity:projects:update', - args=[self.tenant.id]) - - try: - # Avoid the log message in the test output when the workflow's - # step action cannot be instantiated - logging.disable(logging.ERROR) - with self.assertRaises(exceptions.NotFound): - self.client.get(url) - finally: - logging.disable(logging.NOTSET) + # django 1.7 and later does not handle the thrown keystoneclient + # exception well enough. + # TODO(mrunge): re-check when django-1.8 is stable + @unittest.skipIf(django.VERSION >= (1, 7, 0), + 'Currently skipped with Django >= 1.7') + @test.create_stubs({api.keystone: ('get_default_role', + 'tenant_get', + 'domain_get'), + quotas: ('get_tenant_quota_data', + 'get_disabled_quotas')}) + def test_update_project_when_default_role_does_not_exist(self): + project = self.tenants.first() + domain_id = project.domain_id + quota = self.quotas.first() + + api.keystone.get_default_role(IsA(http.HttpRequest)) \ + .MultipleTimes().AndReturn(None) # Default role doesn't exist + api.keystone.tenant_get(IsA(http.HttpRequest), self.tenant.id, + admin=True) \ + .AndReturn(project) + api.keystone.domain_get(IsA(http.HttpRequest), domain_id) \ + .AndReturn(self.domain) + quotas.get_disabled_quotas(IsA(http.HttpRequest)) \ + .AndReturn(self.disabled_quotas.first()) + quotas.get_tenant_quota_data(IsA(http.HttpRequest), + tenant_id=self.tenant.id) \ + .AndReturn(quota) + self.mox.ReplayAll() + + url = reverse('horizon:identity:projects:update', + args=[self.tenant.id]) + + try: + # Avoid the log message in the test output when the workflow's + # step action cannot be instantiated + logging.disable(logging.ERROR) + with self.assertRaises(exceptions.NotFound): + self.client.get(url) + finally: + logging.disable(logging.NOTSET) class UsageViewTests(test.BaseAdminViewTests): diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/views.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -68,6 +68,7 @@ class IndexView(tables.DataTableView): table_class = project_tables.TenantsTable template_name = 'identity/projects/index.html' + page_title = _("Projects") def has_more_data(self, table): return self._more @@ -116,6 +117,7 @@ template_name = 'identity/projects/usage.html' csv_response_class = project_views.ProjectUsageCsvRenderer csv_template_name = 'project/overview/usage.csv' + page_title = _("Project Usage") def get_data(self): super(ProjectUsageView, self).get_data() diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/workflows.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/workflows.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/projects/workflows.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/projects/workflows.py 2015-03-19 19:07:23.000000000 +0000 @@ -41,6 +41,7 @@ PROJECT_GROUP_ENABLED = keystone.VERSIONS.active >= 3 PROJECT_USER_MEMBER_SLUG = "update_members" PROJECT_GROUP_MEMBER_SLUG = "update_group_members" +COMMON_HORIZONTAL_TEMPLATE = "identity/projects/_common_horizontal_form.html" class ProjectQuotaAction(workflows.Action): @@ -123,12 +124,14 @@ class UpdateProjectQuota(workflows.Step): action_class = UpdateProjectQuotaAction + template_name = COMMON_HORIZONTAL_TEMPLATE depends_on = ("project_id",) contributes = quotas.QUOTA_FIELDS class CreateProjectQuota(workflows.Step): action_class = CreateProjectQuotaAction + template_name = COMMON_HORIZONTAL_TEMPLATE depends_on = ("project_id",) contributes = quotas.QUOTA_FIELDS @@ -168,6 +171,7 @@ class CreateProjectInfo(workflows.Step): action_class = CreateProjectInfoAction + template_name = COMMON_HORIZONTAL_TEMPLATE contributes = ("domain_id", "domain_name", "project_id", @@ -550,6 +554,7 @@ class UpdateProjectInfo(workflows.Step): action_class = UpdateProjectInfoAction + template_name = COMMON_HORIZONTAL_TEMPLATE depends_on = ("project_id",) contributes = ("domain_id", "domain_name", diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/roles/templates/roles/create.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/roles/templates/roles/create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/roles/templates/roles/create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/roles/templates/roles/create.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,11 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create Role" %}{% endblock %} -{% block page_header %} - {# to make searchable false, just remove it from the include statement #} - {% include "horizon/common/_page_header.html" with title=_("Create Role") %} -{% endblock page_header %} - {% block main %} {% include 'identity/roles/_create.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/roles/templates/roles/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/roles/templates/roles/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/roles/templates/roles/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/roles/templates/roles/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Roles" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Roles") %} -{% endblock page_header %} - {% block main %} {{ table.render }} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/roles/templates/roles/update.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/roles/templates/roles/update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/roles/templates/roles/update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/roles/templates/roles/update.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,11 +2,6 @@ {% load i18n %} {% block title %}{% trans "Update Role" %}{% endblock %} -{% block page_header %} - {# to make searchable false, just remove it from the include statement #} - {% include "horizon/common/_page_header.html" with title=_("Update Role") %} -{% endblock page_header %} - {% block main %} {% include 'identity/roles/_update.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/roles/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/roles/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/roles/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/roles/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -34,6 +34,7 @@ class IndexView(tables.DataTableView): table_class = project_tables.RolesTable template_name = 'identity/roles/index.html' + page_title = _("Roles") def get_data(self): roles = [] @@ -58,6 +59,7 @@ submit_label = _("Update Role") submit_url = "horizon:identity:roles:update" success_url = reverse_lazy('horizon:identity:roles:index') + page_title = _("Update Role") @memoized.memoized_method def get_object(self): @@ -89,3 +91,4 @@ submit_label = _("Create Role") submit_url = reverse_lazy("horizon:identity:roles:create") success_url = reverse_lazy('horizon:identity:roles:index') + page_title = _("Create Role") diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/forms.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/forms.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/forms.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/forms.py 2015-03-19 19:07:23.000000000 +0000 @@ -18,6 +18,7 @@ import logging +from django.conf import settings from django.forms import ValidationError # noqa from django import http from django.utils.translation import ugettext_lazy as _ @@ -26,6 +27,7 @@ from horizon import exceptions from horizon import forms from horizon import messages +from horizon.utils import functions as utils from horizon.utils import validators from openstack_dashboard import api @@ -35,6 +37,26 @@ PROJECT_REQUIRED = api.keystone.VERSIONS.active < 3 +class PasswordMixin(forms.SelfHandlingForm): + password = forms.RegexField( + label=_("Password"), + widget=forms.PasswordInput(render_value=False), + regex=validators.password_validator(), + error_messages={'invalid': validators.password_validator_msg()}) + confirm_password = forms.CharField( + label=_("Confirm Password"), + widget=forms.PasswordInput(render_value=False)) + no_autocomplete = True + + def clean(self): + '''Check to make sure password fields match.''' + data = super(forms.Form, self).clean() + if 'password' in data: + if data['password'] != data.get('confirm_password', None): + raise ValidationError(_('Passwords do not match.')) + return data + + class BaseUserForm(forms.SelfHandlingForm): def __init__(self, request, *args, **kwargs): super(BaseUserForm, self).__init__(request, *args, **kwargs) @@ -58,19 +80,11 @@ project_choices.insert(0, ('', _("Select a project"))) self.fields['project'].choices = project_choices - def clean(self): - '''Check to make sure password fields match.''' - data = super(forms.Form, self).clean() - if 'password' in data: - if data['password'] != data.get('confirm_password', None): - raise ValidationError(_('Passwords do not match.')) - return data - ADD_PROJECT_URL = "horizon:identity:projects:create" -class CreateUserForm(BaseUserForm): +class CreateUserForm(PasswordMixin, BaseUserForm): # Hide the domain_id and domain_name by default domain_id = forms.CharField(label=_("Domain ID"), required=False, @@ -82,24 +96,19 @@ email = forms.EmailField( label=_("Email"), required=False) - password = forms.RegexField( - label=_("Password"), - widget=forms.PasswordInput(render_value=False), - regex=validators.password_validator(), - error_messages={'invalid': validators.password_validator_msg()}) - confirm_password = forms.CharField( - label=_("Confirm Password"), - widget=forms.PasswordInput(render_value=False)) project = forms.DynamicChoiceField(label=_("Primary Project"), required=PROJECT_REQUIRED, add_item_link=ADD_PROJECT_URL) role_id = forms.ChoiceField(label=_("Role"), required=PROJECT_REQUIRED) - no_autocomplete = True def __init__(self, *args, **kwargs): roles = kwargs.pop('roles') super(CreateUserForm, self).__init__(*args, **kwargs) + # Reorder form fields from multiple inheritance + self.fields.keyOrder = ["domain_id", "domain_name", "name", + "email", "password", "confirm_password", + "project", "role_id"] role_choices = [(role.id, role.name) for role in roles] self.fields['role_id'].choices = role_choices @@ -165,41 +174,24 @@ email = forms.EmailField( label=_("Email"), required=False) - password = forms.RegexField( - label=_("Password"), - widget=forms.PasswordInput(render_value=False), - regex=validators.password_validator(), - required=False, - error_messages={'invalid': validators.password_validator_msg()}) - confirm_password = forms.CharField( - label=_("Confirm Password"), - widget=forms.PasswordInput(render_value=False), - required=False) project = forms.ChoiceField(label=_("Primary Project"), required=PROJECT_REQUIRED) - no_autocomplete = True def __init__(self, request, *args, **kwargs): super(UpdateUserForm, self).__init__(request, *args, **kwargs) if api.keystone.keystone_can_edit_user() is False: - for field in ('name', 'email', 'password', 'confirm_password'): + for field in ('name', 'email'): self.fields.pop(field) - # For keystone V3, display the two fields in read-only + # For keystone V3, display the two fields in read-only if api.keystone.VERSIONS.active >= 3: readonlyInput = forms.TextInput(attrs={'readonly': 'readonly'}) self.fields["domain_id"].widget = readonlyInput self.fields["domain_name"].widget = readonlyInput - # We have to protect the entire "data" dict because it contains the - # password and confirm_password strings. - @sensitive_variables('data', 'password') def handle(self, request, data): user = data.pop('id') - # Throw away the password confirmation, we're done with it. - data.pop('confirm_password', None) - data.pop('domain_id') data.pop('domain_name') try: @@ -218,5 +210,60 @@ if isinstance(response, http.HttpResponse): return response + else: + return True + + +class ChangePasswordForm(PasswordMixin, forms.SelfHandlingForm): + id = forms.CharField(widget=forms.HiddenInput) + name = forms.CharField( + label=_("User Name"), + widget=forms.TextInput(attrs={'readonly': 'readonly'}), + required=False) + + def __init__(self, request, *args, **kwargs): + super(ChangePasswordForm, self).__init__(request, *args, **kwargs) + + if getattr(settings, 'ENFORCE_PASSWORD_CHECK', False): + self.fields["admin_password"] = forms.CharField( + label=_("Admin Password"), + widget=forms.PasswordInput(render_value=False)) + # Reorder form fields from multiple inheritance + self.fields.keyOrder = ["id", "name", "admin_password", + "password", "confirm_password"] + + @sensitive_variables('data', 'password', 'admin_password') + def handle(self, request, data): + user_id = data.pop('id') + password = data.pop('password') + admin_password = None + + # Throw away the password confirmation, we're done with it. + data.pop('confirm_password', None) + + # Verify admin password before changing user password + if getattr(settings, 'ENFORCE_PASSWORD_CHECK', False): + admin_password = data.pop('admin_password') + if not api.keystone.user_verify_admin_password(request, + admin_password): + self.api_error(_('The admin password is incorrect.')) + return False + + try: + response = api.keystone.user_update_password( + request, user_id, password) + if user_id == request.user.id: + return utils.logout_with_message( + request, + _('Password changed. Please log in to continue.'), + redirect=False) + messages.success(request, + _('User password has been updated successfully.')) + except Exception: + response = exceptions.handle(request, ignore=True) + messages.error(request, _('Unable to update the user password.')) + + if isinstance(response, http.HttpResponse): + return response else: return True diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -55,6 +55,19 @@ return api.keystone.keystone_can_edit_user() +class ChangePasswordLink(policy.PolicyTargetMixin, tables.LinkAction): + name = "change_password" + verbose_name = _("Change Password") + url = "horizon:identity:users:change_password" + classes = ("ajax-modal",) + icon = "key" + policy_rules = (("identity", "identity:change_password"),) + policy_target_attrs = (("user_id", "id"),) + + def allowed(self, request, user): + return api.keystone.keystone_can_edit_user() + + class ToggleEnabled(policy.PolicyTargetMixin, tables.BatchAction): name = "toggle" @@ -204,7 +217,7 @@ ("false", False) ) name = tables.Column('name', - link=("horizon:identity:users:detail"), + link="horizon:identity:users:detail", verbose_name=_('User Name'), form_field=forms.CharField(), update_action=UpdateCell) @@ -223,11 +236,14 @@ enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True, status_choices=STATUS_CHOICES, + filters=(defaultfilters.yesno, + defaultfilters.capfirst), empty_value="False") class Meta(object): name = "users" verbose_name = _("Users") - row_actions = (EditUserLink, ToggleEnabled, DeleteUsersAction) + row_actions = (EditUserLink, ChangePasswordLink, ToggleEnabled, + DeleteUsersAction) table_actions = (UserFilterAction, CreateUserLink, DeleteUsersAction) row_class = UpdateRow diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/_change_password.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/_change_password.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/_change_password.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/_change_password.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,16 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Change user's password. We highly recommend you create a strong one." %}

+ +{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/change_password.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/change_password.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/change_password.html 1970-01-01 00:00:00.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/change_password.html 2015-03-19 19:07:23.000000000 +0000 @@ -0,0 +1,7 @@ +{% extends "base.html" %} +{% load i18n %} +{% block title %}{% trans "Change Password" %}{% endblock %} + +{% block main %} + {% include "identity/users/_change_password.html" %} +{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/create.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/create.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,11 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create User" %}{% endblock %} -{% block page_header %} - {# to make searchable false, just remove it from the include statement #} - {% include "horizon/common/_page_header.html" with title=_("Create User") %} -{% endblock page_header %} - {% block main %} {% include 'identity/users/_create.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/detail.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/detail.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/detail.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/detail.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "User Details" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=page_title %} -{% endblock page_header %} - {% block main %}
@@ -13,4 +9,3 @@
{% endblock %} - diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -3,7 +3,7 @@ {% block title %}{% trans "Users" %}{% endblock %} {% block page_header %} - {% include "horizon/common/_domain_page_header.html" with title=_("Users") %} + {% include "horizon/common/_domain_page_header.html" with title=page_title %} {% endblock page_header %} {% block main %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/update.html horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/update.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/templates/users/update.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/templates/users/update.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,11 +2,6 @@ {% load i18n %} {% block title %}{% trans "Update User" %}{% endblock %} -{% block page_header %} - {# to make searchable false, just remove it from the include statement #} - {% include "horizon/common/_page_header.html" with title=_("Update User") %} -{% endblock page_header %} - {% block main %} {% include 'identity/users/_update.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/tests.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/tests.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/tests.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/tests.py 2015-03-19 19:07:23.000000000 +0000 @@ -20,6 +20,7 @@ from django.core.urlresolvers import reverse from django import http +from django.test.utils import override_settings from mox import IgnoreArg # noqa from mox import IsA # noqa @@ -32,6 +33,8 @@ USER_CREATE_URL = reverse('horizon:identity:users:create') USER_UPDATE_URL = reverse('horizon:identity:users:update', args=[1]) USER_DETAIL_URL = reverse('horizon:identity:users:detail', args=[1]) +USER_CHANGE_PASSWORD_URL = reverse('horizon:identity:users:change_password', + args=[1]) class UsersViewTests(test.BaseAdminViewTests): @@ -281,11 +284,10 @@ 'user_update_password', 'user_update', 'roles_for_user', )}) - def _update(self, user): + def test_update(self): user = self.users.get(id="1") domain_id = user.domain_id domain = self.domains.get(id=domain_id) - test_password = 'normalpwd' email = getattr(user, 'email', '') api.keystone.user_get(IsA(http.HttpRequest), '1', @@ -300,7 +302,6 @@ user.id, email=email, name=u'test_user', - password=test_password, project=self.tenant.id).AndReturn(None) self.mox.ReplayAll() @@ -309,9 +310,7 @@ 'id': user.id, 'name': user.name, 'email': email, - 'password': test_password, - 'project': self.tenant.id, - 'confirm_password': test_password} + 'project': self.tenant.id} res = self.client.post(USER_UPDATE_URL, formData) @@ -321,7 +320,6 @@ 'domain_get', 'tenant_list', 'user_update_tenant', - 'user_update_password', 'user_update', 'roles_for_user', )}) def test_update_with_no_email_attribute(self): @@ -341,7 +339,6 @@ user.id, email=user.email, name=user.name, - password=user.password, project=self.tenant.id).AndReturn(None) self.mox.ReplayAll() @@ -350,9 +347,7 @@ 'id': user.id, 'name': user.name, 'email': "", - 'password': user.password, - 'project': self.tenant.id, - 'confirm_password': user.password} + 'project': self.tenant.id} res = self.client.post(USER_UPDATE_URL, formData) @@ -391,63 +386,94 @@ self.assertNoFormErrors(res) self.assertMessageCount(error=1) - @test.create_stubs({api.keystone: ('domain_get', - 'user_get', - 'tenant_list')}) + @test.create_stubs({api.keystone: ('user_get', + 'user_update_password')}) + def test_change_password(self): + user = self.users.get(id="5") + test_password = 'normalpwd' + + api.keystone.user_get(IsA(http.HttpRequest), '1', + admin=True).AndReturn(user) + api.keystone.user_update_password(IsA(http.HttpRequest), + user.id, + test_password).AndReturn(None) + + self.mox.ReplayAll() + + formData = {'method': 'ChangePasswordForm', + 'id': user.id, + 'name': user.name, + 'password': test_password, + 'confirm_password': test_password} + + res = self.client.post(USER_CHANGE_PASSWORD_URL, formData) + + self.assertNoFormErrors(res) + + @test.create_stubs({api.keystone: ('user_get', + 'user_verify_admin_password')}) + @override_settings(ENFORCE_PASSWORD_CHECK=True) + def test_change_password_validation_for_admin_password(self): + user = self.users.get(id="1") + test_password = 'normalpwd' + admin_password = 'secret' + + api.keystone.user_get(IsA(http.HttpRequest), '1', + admin=True).AndReturn(user) + api.keystone.user_verify_admin_password( + IsA(http.HttpRequest), admin_password).AndReturn(None) + + self.mox.ReplayAll() + + formData = {'method': 'ChangePasswordForm', + 'id': user.id, + 'name': user.name, + 'password': test_password, + 'confirm_password': test_password, + 'admin_password': admin_password} + + res = self.client.post(USER_CHANGE_PASSWORD_URL, formData) + + self.assertFormError(res, "form", None, + ['The admin password is incorrect.']) + + @test.create_stubs({api.keystone: ('user_get',)}) def test_update_validation_for_password_too_short(self): user = self.users.get(id="1") - domain_id = user.domain_id - domain = self.domains.get(id=domain_id) api.keystone.user_get(IsA(http.HttpRequest), '1', admin=True).AndReturn(user) - api.keystone.domain_get(IsA(http.HttpRequest), domain_id) \ - .AndReturn(domain) - api.keystone.tenant_list(IgnoreArg(), domain=domain_id, user=user.id) \ - .AndReturn([self.tenants.list(), False]) self.mox.ReplayAll() - formData = {'method': 'UpdateUserForm', + formData = {'method': 'ChangePasswordForm', 'id': user.id, 'name': user.name, - 'email': user.email, 'password': 't', - 'project': self.tenant.id, 'confirm_password': 't'} - res = self.client.post(USER_UPDATE_URL, formData) + res = self.client.post(USER_CHANGE_PASSWORD_URL, formData) self.assertFormError( res, "form", 'password', ['Password must be between 8 and 18 characters.']) - @test.create_stubs({api.keystone: ('domain_get', - 'user_get', - 'tenant_list')}) + @test.create_stubs({api.keystone: ('user_get',)}) def test_update_validation_for_password_too_long(self): user = self.users.get(id="1") - domain_id = user.domain_id - domain = self.domains.get(id=domain_id) api.keystone.user_get(IsA(http.HttpRequest), '1', admin=True).AndReturn(user) - api.keystone.domain_get(IsA(http.HttpRequest), domain_id) \ - .AndReturn(domain) - api.keystone.tenant_list(IgnoreArg(), domain=domain_id, user=user.id) \ - .AndReturn([self.tenants.list(), False]) self.mox.ReplayAll() - formData = {'method': 'UpdateUserForm', + formData = {'method': 'ChangePasswordForm', 'id': user.id, 'name': user.name, - 'email': user.email, 'password': 'ThisIsASuperLongPassword', - 'project': self.tenant.id, 'confirm_password': 'ThisIsASuperLongPassword'} - res = self.client.post(USER_UPDATE_URL, formData) + res = self.client.post(USER_CHANGE_PASSWORD_URL, formData) self.assertFormError( res, "form", 'password', @@ -620,27 +646,20 @@ self.assertTrue(self._is_element_present("id_confirm_password_error"), "Couldn't find password error element.") - @test.create_stubs({api.keystone: ('tenant_list', - 'user_get', - 'domain_get')}) + @test.create_stubs({api.keystone: ('user_get',)}) def test_update_user_with_passwords_not_matching(self): api.keystone.user_get(IsA(http.HttpRequest), '1', admin=True).AndReturn(self.user) - api.keystone.domain_get(IsA(http.HttpRequest), '1') \ - .AndReturn(self.domain) - api.keystone.tenant_list(IgnoreArg(), - domain=self.user.domain_id, - user=self.user.id) \ - .AndReturn([self.tenants.list(), False]) self.mox.ReplayAll() - self.selenium.get("%s%s" % (self.live_server_url, USER_UPDATE_URL)) + self.selenium.get("%s%s" % (self.live_server_url, + USER_CHANGE_PASSWORD_URL)) self.assertFalse(self._is_element_present("id_confirm_password_error"), "Password error element shouldn't yet exist.") self.selenium.find_element_by_id("id_password").send_keys("test") self.selenium.find_element_by_id("id_confirm_password").send_keys("te") - self.selenium.find_element_by_id("id_email").clear() + self.selenium.find_element_by_id("id_name").click() self.assertTrue(self._is_element_present("id_confirm_password_error"), "Couldn't find password error element.") diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/urls.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/urls.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/urls.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/urls.py 2015-03-19 19:07:23.000000000 +0000 @@ -32,4 +32,6 @@ views.UpdateView.as_view(), name='update'), url(r'^create/$', views.CreateView.as_view(), name='create'), url(r'^(?P[^/]+)/detail/$', - views.DetailView.as_view(), name='detail')) + views.DetailView.as_view(), name='detail'), + url(r'^(?P[^/]+)/change_password/$', + views.ChangePasswordView.as_view(), name='change_password')) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/identity/users/views.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/identity/users/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -23,13 +23,13 @@ from django.utils.decorators import method_decorator # noqa from django.utils.translation import ugettext_lazy as _ from django.views.decorators.debug import sensitive_post_parameters # noqa -from django.views import generic from horizon import exceptions from horizon import forms from horizon import messages from horizon import tables from horizon.utils import memoized +from horizon import views from openstack_dashboard import api from openstack_dashboard import policy @@ -43,6 +43,7 @@ class IndexView(tables.DataTableView): table_class = project_tables.UsersTable template_name = 'identity/users/index.html' + page_title = _("Users") def get_data(self): users = [] @@ -78,9 +79,8 @@ submit_label = _("Update User") submit_url = "horizon:identity:users:update" success_url = reverse_lazy('horizon:identity:users:index') + page_title = _("Update User") - @method_decorator(sensitive_post_parameters('password', - 'confirm_password')) def dispatch(self, *args, **kwargs): return super(UpdateView, self).dispatch(*args, **kwargs) @@ -92,7 +92,7 @@ except Exception: redirect = reverse("horizon:identity:users:index") exceptions.handle(self.request, - _('Unable to update user.'), + _('Unable to retrieve user information.'), redirect=redirect) def get_context_data(self, **kwargs): @@ -127,10 +127,10 @@ modal_header = _("Create User") form_id = "create_user_form" form_class = project_forms.CreateUserForm - template_name = 'identity/users/create.html' submit_label = _("Create User") submit_url = reverse_lazy("horizon:identity:users:create") success_url = reverse_lazy('horizon:identity:users:index') + page_title = _("Create User") @method_decorator(sensitive_post_parameters('password', 'confirm_password')) @@ -159,8 +159,9 @@ 'role_id': getattr(default_role, "id", None)} -class DetailView(generic.TemplateView): +class DetailView(views.HorizonTemplateView): template_name = 'identity/users/detail.html' + page_title = _("User Details: {{ user.name }}") def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) @@ -179,7 +180,6 @@ context["user"] = user context["domain_id"] = domain_id context["domain_name"] = domain_name - context["page_title"] = _("User Details: %s") % user.name context["url"] = self.get_redirect_url() context["actions"] = table.render_row_actions(user) return context @@ -198,3 +198,41 @@ def get_redirect_url(self): return reverse('horizon:identity:users:index') + + +class ChangePasswordView(forms.ModalFormView): + template_name = 'identity/users/change_password.html' + modal_header = _("Change Password") + form_id = "change_user_password_form" + form_class = project_forms.ChangePasswordForm + submit_url = "horizon:identity:users:change_password" + submit_label = _("Save") + success_url = reverse_lazy('horizon:identity:users:index') + page_title = _("Change Password") + + @method_decorator(sensitive_post_parameters('password', + 'confirm_password')) + def dispatch(self, *args, **kwargs): + return super(ChangePasswordView, self).dispatch(*args, **kwargs) + + @memoized.memoized_method + def get_object(self): + try: + return api.keystone.user_get(self.request, self.kwargs['user_id'], + admin=True) + except Exception: + redirect = reverse("horizon:identity:users:index") + exceptions.handle(self.request, + _('Unable to retrieve user information.'), + redirect=redirect) + + def get_context_data(self, **kwargs): + context = super(ChangePasswordView, self).get_context_data(**kwargs) + args = (self.kwargs['user_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) + return context + + def get_initial(self): + user = self.get_object() + return {'id': self.kwargs['user_id'], + 'name': user.name} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/api_access/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/api_access/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/api_access/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/api_access/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -21,11 +21,11 @@ from django import shortcuts from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ -from django.views import generic from horizon import exceptions from horizon import forms from horizon import messages +from horizon import views from openstack_dashboard import api @@ -133,8 +133,9 @@ return shortcuts.redirect(request.build_absolute_uri()) -class CredentialsView(forms.ModalFormMixin, generic.TemplateView): +class CredentialsView(forms.ModalFormMixin, views.HorizonTemplateView): template_name = 'project/access_and_security/api_access/credentials.html' + page_title = _("User Credentials Details") def get_context_data(self, **kwargs): context = super(CredentialsView, self).get_context_data(**kwargs) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -73,6 +73,8 @@ name = "release" classes = ('btn-danger',) icon = "unlink" + help_text = _("Once a floating IP is released, there is" + " no guarantee the same IP can be allocated again.") @staticmethod def action_present(count): diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/floating_ips/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/floating_ips/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/floating_ips/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/floating_ips/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -45,7 +45,12 @@ class AllocateView(forms.ModalFormView): form_class = project_forms.FloatingIpAllocate + form_id = "associate_floating_ip_form" + modal_header = _("Allocate Floating IP") template_name = 'project/access_and_security/floating_ips/allocate.html' + submit_label = _("Allocate IP") + submit_url = reverse_lazy( + "horizon:project:access_and_security:floating_ips:allocate") success_url = reverse_lazy('horizon:project:access_and_security:index') def get_object_display(self, obj): diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/keypairs/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/keypairs/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/keypairs/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/keypairs/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -78,6 +78,15 @@ return True +class KeypairsFilterAction(tables.FilterAction): + + def filter(self, table, keypairs, filter_string): + """Naive case-insensitive search.""" + query = filter_string.lower() + return [keypair for keypair in keypairs + if query in keypair.name.lower()] + + class KeypairsTable(tables.DataTable): detail_link = "horizon:project:access_and_security:keypairs:detail" @@ -91,5 +100,6 @@ class Meta(object): name = "keypairs" verbose_name = _("Key Pairs") - table_actions = (CreateKeyPair, ImportKeyPair, DeleteKeyPairs,) + table_actions = (CreateKeyPair, ImportKeyPair, DeleteKeyPairs, + KeypairsFilterAction,) row_actions = (DeleteKeyPairs,) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/keypairs/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/keypairs/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/keypairs/views.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/keypairs/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -24,12 +24,12 @@ from django import http from django.template.defaultfilters import slugify # noqa from django.utils.translation import ugettext_lazy as _ -from django.views.generic import TemplateView # noqa from django.views.generic import View # noqa from horizon import exceptions from horizon import forms from horizon.utils import memoized +from horizon import views from openstack_dashboard import api @@ -39,8 +39,14 @@ class CreateView(forms.ModalFormView): form_class = project_forms.CreateKeypair + form_id = "create_keypair_form" + modal_header = _("Create Key Pair") template_name = 'project/access_and_security/keypairs/create.html' + submit_label = _("Create Key Pair") + submit_url = reverse_lazy( + "horizon:project:access_and_security:keypairs:create") success_url = 'horizon:project:access_and_security:keypairs:download' + page_title = _("Create Key Pair") def get_success_url(self): return reverse(self.success_url, @@ -49,15 +55,22 @@ class ImportView(forms.ModalFormView): form_class = project_forms.ImportKeypair + form_id = "import_keypair_form" + modal_header = _("Import Key Pair") template_name = 'project/access_and_security/keypairs/import.html' + submit_label = _("Import Key Pair") + submit_url = reverse_lazy( + "horizon:project:access_and_security:keypairs:import") success_url = reverse_lazy('horizon:project:access_and_security:index') + page_title = _("Import Key Pair") def get_object_id(self, keypair): return keypair.name -class DetailView(TemplateView): +class DetailView(views.HorizonTemplateView): template_name = 'project/access_and_security/keypairs/detail.html' + page_title = _("Key Pair Details") @memoized.memoized_method def _get_data(self): @@ -75,15 +88,16 @@ def get_context_data(self, **kwargs): """Gets the context data for keypair.""" context = super(DetailView, self).get_context_data(**kwargs) - context['page_title'] = _("Key Pair Details") context['keypair'] = self._get_data() return context -class DownloadView(TemplateView): +class DownloadView(views.HorizonTemplateView): + template_name = 'project/access_and_security/keypairs/download.html' + page_title = _("Download Key Pair") + def get_context_data(self, keypair_name=None): return {'keypair_name': keypair_name} - template_name = 'project/access_and_security/keypairs/download.html' class GenerateView(View): diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/security_groups/forms.py horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/security_groups/forms.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/security_groups/forms.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/security_groups/forms.py 2015-03-19 19:07:23.000000000 +0000 @@ -20,7 +20,6 @@ from django.conf import settings from django.core.urlresolvers import reverse -from django.core import validators from django.forms import ValidationError # noqa from django.utils.translation import ugettext_lazy as _ @@ -33,62 +32,68 @@ from openstack_dashboard.utils import filters -class CreateGroup(forms.SelfHandlingForm): +class GroupBase(forms.SelfHandlingForm): + """Base class to handle creation and update of security groups. + + Children classes must define two attributes: + + .. attribute:: success_message + + A success message containing the placeholder %s, + which will be replaced by the group name. + + .. attribute:: error_message + + An error message containing the placeholder %s, + which will be replaced by the error message. + """ name = forms.CharField(label=_("Name"), max_length=255, - error_messages={ - 'required': _('This field is required.'), - 'invalid': _("The string may only contain" - " ASCII characters and numbers.")}, - validators=[validators.validate_slug]) + validators=[ + utils_validators.validate_printable_ascii]) description = forms.CharField(label=_("Description"), required=False, widget=forms.Textarea(attrs={'rows': 4})) + def _call_network_api(self, request, data): + """Call the underlying network API: Nova-network or Neutron. + + Used in children classes to create or update a group. + """ + raise NotImplementedError() + def handle(self, request, data): try: - sg = api.network.security_group_create(request, - data['name'], - data['description']) - messages.success(request, - _('Successfully created security group: %s') - % data['name']) + sg = self._call_network_api(request, data) + messages.success(request, self.success_message % sg.name) return sg - except Exception: + except Exception as e: redirect = reverse("horizon:project:access_and_security:index") - exceptions.handle(request, - _('Unable to create security group.'), - redirect=redirect) + error_msg = self.error_message % e + exceptions.handle(request, error_msg, redirect=redirect) + + +class CreateGroup(GroupBase): + success_message = _('Successfully created security group: %s') + error_message = _('Unable to create security group: %s') + + def _call_network_api(self, request, data): + return api.network.security_group_create(request, + data['name'], + data['description']) -class UpdateGroup(forms.SelfHandlingForm): +class UpdateGroup(GroupBase): + success_message = _('Successfully updated security group: %s') + error_message = _('Unable to update security group: %s') + id = forms.CharField(widget=forms.HiddenInput()) - name = forms.CharField(label=_("Name"), - max_length=255, - error_messages={ - 'required': _('This field is required.'), - 'invalid': _("The string may only contain" - " ASCII characters and numbers.")}, - validators=[validators.validate_slug]) - description = forms.CharField(label=_("Description"), - required=False, - widget=forms.Textarea(attrs={'rows': 4})) - def handle(self, request, data): - try: - sg = api.network.security_group_update(request, - data['id'], - data['name'], - data['description']) - messages.success(request, - _('Successfully updated security group: %s') - % data['name']) - return sg - except Exception: - redirect = reverse("horizon:project:access_and_security:index") - exceptions.handle(request, - _('Unable to update security group.'), - redirect=redirect) + def _call_network_api(self, request, data): + return api.network.security_group_update(request, + data['id'], + data['name'], + data['description']) class AddRule(forms.SelfHandlingForm): diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py 2015-03-19 19:07:23.000000000 +0000 @@ -128,6 +128,15 @@ return POLICY_CHECK(policy, request, policy_target) +class SecurityGroupsFilterAction(tables.FilterAction): + + def filter(self, table, security_groups, filter_string): + """Naive case-insensitive search.""" + query = filter_string.lower() + return [security_group for security_group in security_groups + if query in security_group.name.lower()] + + class SecurityGroupsTable(tables.DataTable): name = tables.Column("name", verbose_name=_("Name")) description = tables.Column("description", verbose_name=_("Description")) @@ -138,7 +147,7 @@ class Meta(object): name = "security_groups" verbose_name = _("Security Groups") - table_actions = (CreateGroup, DeleteGroup) + table_actions = (CreateGroup, DeleteGroup, SecurityGroupsFilterAction) row_actions = (ManageRules, EditGroup, DeleteGroup) @@ -195,19 +204,21 @@ "security_groups:detail", args=[sg_id]) -def get_remote(rule): +def get_remote_ip_prefix(rule): if 'cidr' in rule.ip_range: if rule.ip_range['cidr'] is None: range = '::/0' if rule.ethertype == 'IPv6' else '0.0.0.0/0' else: range = rule.ip_range['cidr'] return range + ' (CIDR)' - elif 'name' in rule.group: - return rule.group['name'] else: return None +def get_remote_security_group(rule): + return rule.group.get('name') + + def get_port_range(rule): ip_proto = rule.ip_protocol if rule.from_port == rule.to_port: @@ -255,7 +266,11 @@ filters=(filter_protocol,)) port_range = tables.Column(get_port_range, verbose_name=_("Port Range")) - remote = tables.Column(get_remote, verbose_name=_("Remote")) + remote_ip_prefix = tables.Column(get_remote_ip_prefix, + verbose_name=_("Remote IP Prefix")) + remote_security_group = tables.Column(get_remote_security_group, + verbose_name=_("Remote Security" + " Group")) def sanitize_id(self, obj_id): return filters.get_int_or_uuid(obj_id) diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py 2015-03-19 19:07:23.000000000 +0000 @@ -35,6 +35,16 @@ SG_CREATE_URL = reverse('horizon:project:access_and_security:' 'security_groups:create') +SG_VIEW_PATH = 'horizon:project:access_and_security:security_groups:%s' +SG_DETAIL_VIEW = SG_VIEW_PATH % 'detail' +SG_UPDATE_VIEW = SG_VIEW_PATH % 'update' +SG_ADD_RULE_VIEW = SG_VIEW_PATH % 'add_rule' + +SG_TEMPLATE_PATH = 'project/access_and_security/security_groups/%s' +SG_DETAIL_TEMPLATE = SG_TEMPLATE_PATH % 'detail.html' +SG_CREATE_TEMPLATE = SG_TEMPLATE_PATH % 'create.html' +SG_UPDATE_TEMPLATE = SG_TEMPLATE_PATH % '_update.html' + def strip_absolute_base(uri): return uri.split(settings.TESTSERVER, 1)[-1] @@ -46,12 +56,9 @@ def setUp(self): super(SecurityGroupsViewTests, self).setUp() sec_group = self.security_groups.first() - self.detail_url = reverse('horizon:project:access_and_security:' - 'security_groups:detail', - args=[sec_group.id]) - self.edit_url = reverse('horizon:project:access_and_security:' - 'security_groups:add_rule', - args=[sec_group.id]) + self.detail_url = reverse(SG_DETAIL_VIEW, args=[sec_group.id]) + self.edit_url = reverse(SG_ADD_RULE_VIEW, args=[sec_group.id]) + self.update_url = reverse(SG_UPDATE_VIEW, args=[sec_group.id]) @test.create_stubs({api.network: ('security_group_rule_create', 'security_group_list', @@ -83,66 +90,77 @@ api.network.security_group_get(IsA(http.HttpRequest), sec_group.id).AndReturn(sec_group) self.mox.ReplayAll() - - res = self.client.get(reverse('horizon:project:access_and_security:' - 'security_groups:update', - args=[sec_group.id])) - self.assertTemplateUsed( - res, 'project/access_and_security/security_groups/_update.html') + res = self.client.get(self.update_url) + self.assertTemplateUsed(res, SG_UPDATE_TEMPLATE) self.assertEqual(res.context['security_group'].name, sec_group.name) @test.create_stubs({api.network: ('security_group_update', 'security_group_get')}) def test_update_security_groups_post(self): - sec_group = self.security_groups.get(name="other_group") - api.network.security_group_update(IsA(http.HttpRequest), - str(sec_group.id), - sec_group.name, - sec_group.description) \ - .AndReturn(sec_group) - api.network.security_group_get(IsA(http.HttpRequest), - sec_group.id).AndReturn(sec_group) - self.mox.ReplayAll() + """Ensure that we can change a group name. - formData = {'method': 'UpdateGroup', - 'id': sec_group.id, - 'name': sec_group.name, - 'description': sec_group.description} - - update_url = reverse('horizon:project:access_and_security:' - 'security_groups:update', - args=[sec_group.id]) - res = self.client.post(update_url, formData) + The name must not be restricted to alphanumeric characters. + bug #1233501 Security group names cannot contain at characters + bug #1224576 Security group names cannot contain spaces + """ + sec_group = self.security_groups.first() + sec_group.name = "@new name" + api.network.security_group_update( + IsA(http.HttpRequest), + str(sec_group.id), + sec_group.name, + sec_group.description).AndReturn(sec_group) + api.network.security_group_get( + IsA(http.HttpRequest), sec_group.id).AndReturn(sec_group) + self.mox.ReplayAll() + form_data = {'method': 'UpdateGroup', + 'id': sec_group.id, + 'name': sec_group.name, + 'description': sec_group.description} + res = self.client.post(self.update_url, form_data) self.assertRedirectsNoFollow(res, INDEX_URL) def test_create_security_groups_get(self): res = self.client.get(SG_CREATE_URL) - self.assertTemplateUsed( - res, 'project/access_and_security/security_groups/create.html') + self.assertTemplateUsed(res, SG_CREATE_TEMPLATE) - @test.create_stubs({api.network: ('security_group_create',)}) def test_create_security_groups_post(self): sec_group = self.security_groups.first() - api.network.security_group_create(IsA(http.HttpRequest), - sec_group.name, - sec_group.description) \ - .AndReturn(sec_group) + self._create_security_group(sec_group) + + def test_create_security_groups_special_chars(self): + """Ensure that a group name is not restricted to alphanumeric + characters. + + bug #1233501 Security group names cannot contain at characters + bug #1224576 Security group names cannot contain spaces + """ + sec_group = self.security_groups.first() + sec_group.name = '@group name' + self._create_security_group(sec_group) + + @test.create_stubs({api.network: ('security_group_create',)}) + def _create_security_group(self, sec_group): + api.network.security_group_create( + IsA(http.HttpRequest), + sec_group.name, + sec_group.description).AndReturn(sec_group) self.mox.ReplayAll() - formData = {'method': 'CreateGroup', - 'name': sec_group.name, - 'description': sec_group.description} - res = self.client.post(SG_CREATE_URL, formData) + form_data = {'method': 'CreateGroup', + 'name': sec_group.name, + 'description': sec_group.description} + res = self.client.post(SG_CREATE_URL, form_data) self.assertRedirectsNoFollow(res, INDEX_URL) @test.create_stubs({api.network: ('security_group_create',)}) def test_create_security_groups_post_exception(self): sec_group = self.security_groups.first() - api.network.security_group_create(IsA(http.HttpRequest), - sec_group.name, - sec_group.description) \ - .AndRaise(self.exceptions.nova) + api.network.security_group_create( + IsA(http.HttpRequest), + sec_group.name, + sec_group.description).AndRaise(self.exceptions.nova) self.mox.ReplayAll() formData = {'method': 'CreateGroup', @@ -153,17 +171,22 @@ self.assertRedirectsNoFollow(res, INDEX_URL) @test.create_stubs({api.network: ('security_group_create',)}) - def test_create_security_groups_post_wrong_name(self): - sec_group = self.security_groups.first() - fail_name = sec_group.name + ' invalid' - self.mox.ReplayAll() - - formData = {'method': 'CreateGroup', - 'name': fail_name, - 'description': sec_group.description} - res = self.client.post(SG_CREATE_URL, formData) - self.assertTemplateUsed( - res, 'project/access_and_security/security_groups/create.html') + def test_create_security_groups_non_printable(self): + """Ensure that group names can only contain printable + ASCII characters. + + Only 95 characters are allowed: from 0x20 (space) to 0x7E (~). + """ + sec_group = self.security_groups.first() + # 0x7F is a control character (DELETE) + fail_name = sec_group.name + ' \x7F' + self.mox.ReplayAll() + + form_data = {'method': 'CreateGroup', + 'name': fail_name, + 'description': sec_group.description} + res = self.client.post(SG_CREATE_URL, form_data) + self.assertTemplateUsed(res, SG_CREATE_TEMPLATE) self.assertContains(res, "ASCII") @test.create_stubs({api.network: ('security_group_get',)}) @@ -174,17 +197,15 @@ sec_group.id).AndReturn(sec_group) self.mox.ReplayAll() res = self.client.get(self.detail_url) - self.assertTemplateUsed( - res, 'project/access_and_security/security_groups/detail.html') + self.assertTemplateUsed(res, SG_DETAIL_TEMPLATE) @test.create_stubs({api.network: ('security_group_get',)}) def test_detail_get_exception(self): sec_group = self.security_groups.first() - api.network.security_group_get(IsA(http.HttpRequest), - sec_group.id) \ - .AndRaise(self.exceptions.nova) - + api.network.security_group_get( + IsA(http.HttpRequest), + sec_group.id).AndRaise(self.exceptions.nova) self.mox.ReplayAll() res = self.client.get(self.detail_url) @@ -630,12 +651,9 @@ self.security_group_rules = self.security_group_rules_uuid sec_group = self.security_groups.first() - self.detail_url = reverse('horizon:project:access_and_security:' - 'security_groups:detail', - args=[sec_group.id]) - self.edit_url = reverse('horizon:project:access_and_security:' - 'security_groups:add_rule', - args=[sec_group.id]) + self.detail_url = reverse(SG_DETAIL_VIEW, args=[sec_group.id]) + self.edit_url = reverse(SG_ADD_RULE_VIEW, args=[sec_group.id]) + self.update_url = reverse(SG_UPDATE_VIEW, args=[sec_group.id]) def tearDown(self): self.security_groups = self._sec_groups_orig @@ -656,12 +674,9 @@ self.security_group_rules = self.q_secgroup_rules sec_group = self.security_groups.first() - self.detail_url = reverse('horizon:project:access_and_security:' - 'security_groups:detail', - args=[sec_group.id]) - self.edit_url = reverse('horizon:project:access_and_security:' - 'security_groups:add_rule', - args=[sec_group.id]) + self.detail_url = reverse(SG_DETAIL_VIEW, args=[sec_group.id]) + self.edit_url = reverse(SG_ADD_RULE_VIEW, args=[sec_group.id]) + self.update_url = reverse(SG_UPDATE_VIEW, args=[sec_group.id]) def tearDown(self): self.security_groups = self._sec_groups_orig diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/security_groups/views.py horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/security_groups/views.py --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/security_groups/views.py 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/security_groups/views.py 2015-03-19 19:07:23.000000000 +0000 @@ -40,6 +40,8 @@ class DetailView(tables.DataTableView): table_class = project_tables.RulesTable template_name = 'project/access_and_security/security_groups/detail.html' + page_title = _("Manage Security Group Rules: " + "{{ security_group.name }} ({{ security_group.id }})") @memoized.memoized_method def _get_data(self): @@ -61,20 +63,20 @@ def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) - security_group = self._get_data() - security_group_name_id = "%s (%s)" % (security_group.name, - security_group.id) - context["security_group"] = security_group - context["page_title"] = _( - "Manage Security Group Rules: %(security_group)s") % { - 'security_group': security_group_name_id} + context["security_group"] = self._get_data() return context class UpdateView(forms.ModalFormView): form_class = project_forms.UpdateGroup + form_id = "update_security_group_form" + modal_header = _("Edit Security Group") + modal_id = "update_security_group_modal" template_name = 'project/access_and_security/security_groups/update.html' + submit_label = _("Edit Security Group") + submit_url = "horizon:project:access_and_security:security_groups:update" success_url = reverse_lazy('horizon:project:access_and_security:index') + page_title = _("Edit Security Group") @memoized.memoized_method def get_object(self): @@ -89,6 +91,8 @@ def get_context_data(self, **kwargs): context = super(UpdateView, self).get_context_data(**kwargs) context["security_group"] = self.get_object() + args = (self.kwargs['security_group_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) return context def get_initial(self): @@ -100,7 +104,13 @@ class AddRuleView(forms.ModalFormView): form_class = project_forms.AddRule + form_id = "create_security_group_rule_form" + modal_header = _("Add Rule") + modal_id = "create_security_group_rule_modal" template_name = 'project/access_and_security/security_groups/add_rule.html' + submit_label = _("Add") + submit_url = "horizon:project:access_and_security:security_groups:add_rule" + page_title = _("Add Rule") def get_success_url(self): sg_id = self.kwargs['security_group_id'] @@ -110,6 +120,8 @@ def get_context_data(self, **kwargs): context = super(AddRuleView, self).get_context_data(**kwargs) context["security_group_id"] = self.kwargs['security_group_id'] + args = (self.kwargs['security_group_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) return context def get_initial(self): @@ -139,5 +151,12 @@ class CreateView(forms.ModalFormView): form_class = project_forms.CreateGroup + form_id = "create_security_group_form" + modal_header = _("Create Security Group") + modal_id = "create_security_group_modal" template_name = 'project/access_and_security/security_groups/create.html' + submit_label = _("Create Security Group") + submit_url = reverse_lazy( + "horizon:project:access_and_security:security_groups:create") success_url = reverse_lazy('horizon:project:access_and_security:index') + page_title = _("Create Security Group") diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/credentials.html horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/credentials.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/credentials.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/credentials.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "User Credentials" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("User Credentials Details") %} -{% endblock page_header %} - {% block main %} {% include 'project/access_and_security/api_access/_credentials.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/openrc.sh.template horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/openrc.sh.template --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/openrc.sh.template 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/openrc.sh.template 2015-03-19 19:07:23.000000000 +0000 @@ -18,6 +18,7 @@ # as the entity that owns the resources. export OS_TENANT_ID={{ tenant_id }} export OS_TENANT_NAME="{{ tenant_name|shellfilter }}" +export OS_PROJECT_NAME="{{ tenant_name|shellfilter }}" # In addition to the owning entity (tenant), OpenStack stores the entity # performing the action as the **user**. diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/floating_ips/_allocate.html horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/floating_ips/_allocate.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/floating_ips/_allocate.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/floating_ips/_allocate.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,30 +1,18 @@ {% extends "horizon/common/_modal_form.html" %} - {% load horizon i18n %} -{% load url from future %} - -{% block form_id %}associate_floating_ip_form{% endblock %} -{% block form_action %}{% url 'horizon:project:access_and_security:floating_ips:allocate' %}{% endblock %} -{% block modal-header %}{% trans "Allocate Floating IP" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

{% trans "Allocate a floating IP from a given floating IP pool." %}

- -

{% trans "Project Quotas" %}

-
- {% trans "Floating IP" %} ({{ usages.floating_ips.used }}) -

{{ usages.floating_ips.available|quota }}

-
-
-
+{% block modal-body-right %} +
+

{% trans "Description:" %}

+

{% trans "Allocate a floating IP from a given floating IP pool." %}

+ +

{% trans "Project Quotas" %}

+
+ {% trans "Floating IP" %} ({{ usages.floating_ips.used }}) +

{{ usages.floating_ips.available|quota }}

+
+
+
{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} -{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/index.html horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/index.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/index.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/index.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Access & Security" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Access & Security") %} -{% endblock page_header %} - {% block main %}
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/_create.html horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/_create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/_create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/_create.html 2015-03-19 19:07:23.000000000 +0000 @@ -1,26 +1,8 @@ {% extends "horizon/common/_modal_form.html" %} {% load i18n %} -{% load url from future %} -{% block form_id %}create_keypair_form{% endblock %} -{% block form_action %}{% url 'horizon:project:access_and_security:keypairs:create' %}{% endblock %} - -{% block modal-header %}{% trans "Create Key Pair" %}{% endblock %} - -{% block modal-body %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

{% trans "Key pairs are ssh credentials which are injected into images when they are launched. Creating a new key pair registers the public key and downloads the private key (a .pem file)." %}

-

{% trans "Protect and use the key as you would any normal ssh private key." %}

-
-{% endblock %} - -{% block modal-footer %} - - {% trans "Cancel" %} +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Key pairs are ssh credentials which are injected into images when they are launched. Creating a new key pair registers the public key and downloads the private key (a .pem file)." %}

+

{% trans "Protect and use the key as you would any normal ssh private key." %}

{% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/create.html horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/create.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/create.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/create.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,10 +2,6 @@ {% load i18n %} {% block title %}{% trans "Create Key Pair" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create Key Pair") %} -{% endblock page_header %} - {% block main %} {% include 'project/access_and_security/keypairs/_create.html' %} {% endblock %} diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/detail.html horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/detail.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/detail.html 2015-02-05 15:52:24.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/detail.html 2015-03-19 19:07:23.000000000 +0000 @@ -2,9 +2,6 @@ {% load i18n sizeformat %} {% block title %}{% trans "Key Pair Details" %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=page_title %} -{% endblock page_header %} {% block main %}
diff -Nru horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/download.html horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/download.html --- horizon-2015.1~b2/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/download.html 2015-02-05 15:52:19.000000000 +0000 +++ horizon-2015.1~b3/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/keypairs/download.html 2015-03-19 19:07:23.000000000 +0000 @@ -3,10 +3,6 @@ {% load url from future %} {% block title %}{% blocktrans %}Download Key Pair{% endblocktrans %}{% endblock %} -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Download Key Pair") %} -{% endblock page_header %} - {% block main %}