159 Commits

Author SHA1 Message Date
Jenkins
b5e0bd745e Merge "Navigational Breadcrumb should have contextual class" 2016-08-06 07:50:19 +00:00
Rob Cresswell
0e957dd41a Add Angular Schema Form
This patch adds Angular Schema Form[1] and its requirements to Horizon.
There are a number of advantages to this over the current methods of
defining forms and workflows:

- All fields have an individual template, making theming improvements,
  bug fixes, and bootstrap conformity easier.
- The file and line count, especially for workflows, is dramatically
  reduced. The Create Net workflow, for example, goes from 12+ files to
  2, with a big reduction in boilerplate HTML.
- All field validation messages are standardised, so we can match them
  across Horizon and plugins

What this patch contains:
- Many common form fields, including things like the themable checkboxes
  and selects.
- A basic modal template that can be passed with ui-bootstraps $modal
  service to take advantage of schema-form

Next steps:
- Remove the other modal templates so we can standardise. A single
  template opened from the $modal service is fine, and we shouldn't need
  several directives. In this case, we should deprecate them, as the
  modal forms will be used elsewhere.
- Map commonly used form items, like transfer tables, to a schema form
  type like array (they serve similar purposes, so maybe thats what
  should be replaced)
- Use themable selects instead of regular ones

1. http://schemaform.io/

Co-Authored-By: Tyr Johanson <tyr@hpe.com>
Implements: blueprint angular-schema-form
Change-Id: Ib22b2d0db2c4d4775fdef62a180cc994e8ae6280
2016-08-04 16:53:41 +00:00
Tyr Johanson
fca46ab60f Pre-populate the Angular template cache and allow template overrides
This patch populates the Angular template cache from Django.
This eliminates the need for Angular to do an http get for every HTML
fragment.

In addition, now that we are filling the template cache, this patch
introduces the logic needed to override any Angular template HTML from
the current theme.

How it works:
A new template tag is created called "template_cache_preloads". This
tag is used in _scripts.html to generate a list of text/javascript
script tags, each one containing an Angular "run" method that loads
a template contents into the Angular template cache. The first time
any Horizon page is loaded after server start, the template cache
preloads are computed for the current theme.

The output of this tag is cached for 30 days in Django using the
"cache" tag. Further, that cached result is wrapped in a "compress js"
tag to collapse the individual <script> tags into 1 block of
javascript, and compress like all other javascript Horizon serves to
the client.

Finally, when using offline compression, the compressor evaluates the
nodelist (HTML content) of _scripts.html, notices the compress tag
and builds the template cache preloads for each possible theme. Later,
at runtime, when the preloads are generated for the current theme, the
compressor gets the result from the Django cache, and hashes the
contents to determine which manifest file to serve to the client.
Since the preloads generated at run-time are identical to those
generated off-line, the compressor hash matches an existing manifest
which is served to the client.

Notice that even though the template cache pre-loads are generated
off-line...the template_cache_preloads tag will be executed once
every 30 days anyway. However, since the result matches the off-line
compression, the existing manifest continues to be served to the client.

Finally, this patch ALSO watches for 'post_compress' signals. If it
detects that the angular template preloads have been re-compressed, it
clears the old version from the Django cache.

To test the template caching:
- Run horizon
- View page source
- Notice the new <script type="text/javascript"> tags contained in
  the body (only visible if COMPRESS_ENABLED=False
- Open the javascript inspector
- Load launch instance
- Notice there are no longer http calls to load each HTML fragment
  used by the Angular launch instance

To test the override:
- Set the DEFAULT_THEME='material'
- Create /horizon/openstack_dashboard/themes/material/\
static/templates/framework/widgets/help-panel/help-panel.html
- Set the content to <h1>TEST</h1>
- Run Horizon and open launch instance.
- The help content should contain "TEST"

To test the new template tag:
- set a breakpoint or print in angular.py:template_cache_preloads
  and observe when it is called during off-line or run-time use

Co-Authored-By: Diana Whitten <hurgleburgler@gmail.com>

Implements: blueprint angular-template-overrides
Change-Id: I0e4e2623be58abbc68c6e02b2e9c5d7cdaba8e4d
2016-07-13 15:38:07 -07:00
Diana Whitten
313126a806 Navigational Breadcrumb should have contextual class
Closes-bug: #1588456

Change-Id: Ib39096f6d5117ddc6d924c97c273ac58534fab57
2016-06-02 10:14:10 -07:00
Jenkins
77955d5fce Merge "Pass page_url instead of whole request to render" 2016-06-01 05:28:21 +00:00
Bo Wang
0c91293a79 Pass page_url instead of whole request to render
only page url is required to be rendered in template.
move more logic code back to python.

Change-Id: I9f52d030f65084e760f37a439de02b73ef94497f
2016-05-31 10:34:52 +00:00
Timur Sufiev
408e358f79 Move out integration tests scaffolds from Horizon production code
The main idea of this change is to not include into Horizon production
code little helper features we need Horizon to have in gating so
Selenium integration tests could easily navigate through it.

Closes-Bug: #1560467
Change-Id: Ib0c973932260ddfe7f1d2cf9fdab7bbbfe298bea
2016-05-31 10:01:21 +00:00
Jenkins
66658483b2 Merge "Use 'trimmed' for blocktrans with multi-line contents" 2016-05-24 14:59:23 +00:00
Jenkins
d11b46f83c Merge "Add module ngFileUpload as a dependency to main horizon.app module" 2016-05-19 23:12:47 +00:00
Akihiro Motoki
926a4513ea Use 'trimmed' for blocktrans with multi-line contents
blocktrans trimmed option removes newlines from contents of blocktrans
and replaces any whitespace at the beginning and end of a line into
a single space. This makes translator life easier.
More detail is described in the bug report.

Closes-Bug: #1583757
Depends-On: I534895be0d5f4bf0429b1511ee5421991386c873
Change-Id: Idb451b7688fd5533a8315399de13bac8b8078a67
2016-05-19 20:00:40 +00:00
Timur Sufiev
193756fa6c Add module ngFileUpload as a dependency to main horizon.app module
Change-Id: I8ca6379ff6aad32ce934720f234d955dd99204dd
2016-05-19 13:46:55 +03:00
Jenkins
fc06637e4a Merge "Generic details display framework" 2016-05-17 16:39:14 +00:00
Matt Borland
11968c840c Generic details display framework
This patch provides the ability for the registered detail views for
any resource type to be generically presented.

This patch does the following:
 * Adds a directive that displays a set of views (i.e. details sub-views)
 * Adds a Generic Detail display for routed pages
 * Adds the concept of a Descriptor which contains a resource type name
   and an identifier.  The identifier can be something as simple as
   a string, but may also be an object (if the resource type needs more
   than one value to look up its data, e.g. Pool Members)
 * Adds the ability for a resource type to have knowledge about how one
   of its items may be loaded, so any detail page can fetch the information
   given a basic context
 * Adds a generic Angular page (since they all just route to ng-views).
   We will see this used in subsequent patches as well.
 * Sets up a Django route to a non-navigational panel for the Details

Change-Id: Ie116b52ba196f9240fdc6bbc4a12d37beb9b9fcf
Partially-Implements: blueprint angular-registry
2016-05-16 11:39:57 -06:00
David Lyle
dd80909edf Adding old topology to compliment new
There has been user/operator feedback that the new network topology
satisfies different needs than the old did. The two are actually
complimentary rather than mutually exclusive. This patch allows for both
topologies to be visible on separate tabs. Both views share the same
data model, but renders that data differently.

One interesting inclusion is triggering a resize event if an HTML
element with the d3-container CSS class is present. The reason for this
is that the svg content in a non-visible tab is rendered into a container
with 0 height and width. This causes the contents to all sit in the top
left corner of the container. Without the resize event which is used to
trigger a redo of the force layout. The plus side is now this d3 based
network topology handles window resize events. I am open to suggestions
so that a resize event is not necessary on the tab show event.

An additional area for improvement is the inline CSS in
_svg_element.html

Implements blueprint: restore-old-net-topology
Change-Id: Iba6e6ad07b9ff7705f62cdb0281904880df6e4ba
2016-05-11 13:49:46 -06:00
Rob Cresswell
d5b24d7b97 Use breadcrumb nav across Horizon
We've added breadcrumbs to the Details pages, but its a pretty
inconsistent experience. This patch adds breadcrumbs to the base
template, making the breadcrumbs consistent across all pages; this will
be useful when the side nav is hidden for responsive design. This patch
also makes the breadcrumbs on the detail pages behave better with
theming.

- Made the detail header and actions avoid using floats
- Moved breadcrumb truncating to the front end, so that it can be
  customised easily via CSS
- Manually added breadcrumb for ngcontainers page
- Removed overly specific HTML check from Key Pair Details test
- Fixed <dt> alignment on Key Pair Details page

Closes-Bug: 1554812
Partially-Implements: blueprint navigation-improvements
Change-Id: Ibcd4c369b5d8ad62f7c839c0deeaefc750677b40
2016-05-05 23:12:59 +00:00
Diana Whitten
8b76b5fb90 Initial Clean Up Old Styles in _debt.scss
Unnecessary Styles:
 * #create_container_form .modal-footer
 * .list-bullet
 * dt

Can't find usage:
 * .btn-inline
 * #actions.single
 * div.input input[type="checkbox"]
 * .item_detail

Not used any longer:
 * .modal-body fieldset .form-field select[data-add-item-url

Change-Id: Ibfeeec84a58d4dffaa669fc48f27cac02a2011c7
Partially-implements: blueprint horizon-theme-css-reorg
2016-04-29 16:11:17 +00:00
Jenkins
d6f317a5ab Merge "Change projects redirects to overview page" 2016-03-16 02:14:47 +00:00
space
edeb9ddaf9 Change projects redirects to overview page
I read the code of switch region and modify the code of switch project.
 Now change project will redirect to the original page.

Change-Id: Ia19eaefa5de884ac4d5900c93138922f00e9c692
Closes-Bug: #1549560
2016-03-12 11:17:21 +08:00
Richard Jones
900fe7dfd9 Remove default event handler from theme switch links
On Chrome the default <a> event handler was firing after reload()
resulting in a change of location.

This was particularly noticeable on the new angular Swift UI.

Change-Id: I5b644f3b899a5a8c598ceae7cdb76a1e9fd89ebf
Closes-Bug: 1555429
2016-03-10 14:57:21 +11:00
Diana Whitten
c9de52d6bb Dynamic Themes
Horizon themes are now configurable at a user level, through the use
of cookies. The themes that can be set are configurable at a
deployment level through settings.py. Horizon can be configured to
run with multiple themes, and allow users to choose which themes
they wish to run.

Django Compressor:
In order to support dynamic themes, each theme configuration must
be pre-compiled through the Django compressor. By making use of its
built in COMPRESS_OFFLINE_CONTEXT, we now return a generator to
create each of the theme's necessary offline contexts.

Templates:
Horizon themes allowed template overrides via their 'templates'
subfolder.  In order to maintain this parity, a custom theme template
loader was created.  It is run before the other loads, and simply
looks for a Django template in the current theme (cookie driven)
before diverting to the previous template loaders.

Static Files:
Horizon themes allowed static overrides of the images in
'dashboard/img' folder.  A template tag, 'themable_asset' was created
to maintain this parity. Any asset that is wished to be made themable,
given that it is located in Horizon's 'static/dashboard' folder, can
now be made ot be themable.  By making this a template tag, this
gives the developers more granular control over what branders can
customize.

Angular and Plugins:
By far, the trickiest part of this task, Angular and Plugins are
dynamic in the files that they 'discover'.  SCSS is not flexible in
this manner at ALL.  SCSS disallows the importation of a variable
name.  To get around this, themes.scss was created as a Django
template.  This template is the top level import file for all styles
within Horizon, and therefore, allows ALL the scss files to share a
common namespace and thus, can use shared variables as well as extend
shared styles.

Other:
This change is fundamental, in that it changes the method by which
Horizon ingests its SCSS files.  Many problems existing in the
previous implementation, in an effort to make Horizon flexible, its
SCSS was made very inflexible.  This patch corrects those problems.

Change-Id: Ic48b4b5c1d1a41f1e01a8d52784c9d38d192c8f1
Implements: blueprint horizon-dynamic-theme
Closes-Bug: #1480427
2016-02-25 09:49:43 -08:00
Diana Whitten
0bfca75f45 Context Picker should inherit BS dropdown styles
The first step in the dynamic theme effort requires that the context
selection picker inherit properly from a dropdown menu, so that the
styles can be shared and the experience is matched.

Dynamic themes will use the 'select' experience of the context menu
but from within the user menu, so it was necessary to match the
experiences to minimize duplicated code.

The style of the context menu was extremely dependant on DOM structure
and therefore difficult to customize. This has been simplified by using
classes and attempting to keep specificity as low as possible.

Change-Id: Idb9e8f5c1d246688418f68e12fb53f094c01ea34
Partially-implements: blueprint horizon-dynamic-theme
Partially-Implements: blueprint bootstrap-html-standards
2016-02-10 04:23:15 +00:00
Jenkins
4493ade2f0 Merge "Set proper z-index value on messages and navbar" 2016-02-10 02:01:59 +00:00
Diana Whitten
4b5886d276 Branding: Nav icon spacing should use css
The padding around the Top Nav Bar's Dropdown Icons have been done
with actual text spaces, instead of relying on padding that is set
via css. This makes it very difficult to customize the padding
around these elements at a global level.

Some contextual classes have been added for ease of branding-level
customization.

Change-Id: I6768135351637db8a950a4b44366880817ce2df3
Closes-bug: #1537608
2016-02-07 16:50:36 +00:00
Jenkins
c6f75a224d Merge "Branding: Context selector delim should be icon" 2016-02-06 21:03:07 +00:00
Jenkins
0ab863e965 Merge "Removing container ID element" 2016-02-06 10:57:04 +00:00
Diana Whitten
21c94bd218 Removing container ID element
Removing the element, as it was not really needed at all.
Also, removed a debug statement that was discovered in
material.hamburger.js while refactoring for this change.

Added a 'container-fluid' class to the content body
as well, because it is not possible to configure Horizon
as a non-fluid layout without a container element around
the first 'row' element.

Change-Id: I097ea31f991e8d15987b7cf54b77a309d6155771
Closes-bug: #1368924
2016-02-03 21:39:08 +00:00
LIU Yulong
5fb2ce80c6 Set proper z-index value on messages and navbar
The navbar should have been using the built in navbar-fixed-top class
as it is a fixed top navbar.  After added the correct class, the
correct z-index value was added via Bootstrap, then the messages
container simply needed to add a z-index that would appropriately
place it above dropdowns.

The user menu was simply a symptom of the overall problem, where all
the dropdowns in the navbar needed to behave the same way.

The z-index variables of the theme were used, therefore the
incorrect stacking of the messages over the spinner and the modal
were fixed at the same time.

It was noted that the right padding of the messages was using the
incorrect padding variable.  This was corrected.

Closes-bug: #1408640
Co-Authored-By: Diana Whitten <hurgleburgler@gmail.com>
Change-Id: I1d59049d43e74c2a897673307593993f4291da39
2016-02-02 06:05:16 +00:00
Matt Borland
85d44f9f56 Adding Magic Search codebase to Horizon
Magic Search is getting integrated into the Horizon codebase as agreed
at the Tokyo summit.  This Magic Search patch replaces the XStatic
package with its own module, and otherwise works as the current XStatic
package (changes will be made to accommodate Searchlight features in
following patches).  This is not simply a copy of what is in GitHub,
because there were no tests and little documentation.

The code underwent significant restructuring to accommodate the linter and
other standards used in Horizon.

Change-Id: I9a2b0f3fed1955680a534e8d284da2c8ee68ef16
Implements: blueprint integrate-magic-search
2016-01-29 14:05:52 +00:00
Jenkins
2b5941ca42 Merge "Escape RegEx characters from table quick search text" 2016-01-26 21:46:04 +00:00
Diana Whitten
a9bd8b2335 Branding: Context selector delim should be icon
The delimiter in the context selector should use an icon to enable
vast customization options.

Change-Id: I07551471213e79b6f7d6e1c95ccc7a605c2a9418
Closes-bug: #1537594
2016-01-24 21:37:19 -07:00
Jenkins
38f6764508 Merge "Branding: Horizon Header should be more composable" 2016-01-21 13:57:20 +00:00
Jenkins
2832c396e1 Merge "Set <base> element ONLY for Angular Panels" 2016-01-19 13:33:28 +00:00
Jenkins
0741ef7ab3 Merge "Revert "Specify <base> element in all pages"" 2016-01-16 14:16:08 +00:00
Diana Whitten
378623217d Branding: Horizon Header should be more composable
A recent branding effort exposed several aspects of the header
templates that need to be more composable to enable richer
customizations.

The Horizon _header.html template has many components within it:
region pickers, user menus, logos. All of these things are highly
desirable to customize, but because it is all contained in the same
template, a brander will have to override the entire template. This
is not ideal as it increase the cost of maintenance.

The splash section of the login page, the context selector, the logo
section of the header, the user menu and the region selector are now
composable to a much more granular level.  The top navigation menus
can now be used outside of existing within a list.  The templates
now allow use as normal, standalone dropdown items.

Also, the current primary color used for 'material' was highly
unpleasant with OpenStack Red.  Another color on the material design
palette was chosen, very close to the previous blue. See
https://www.google.com/design/spec/style/color.html#color-color-palette

To showcase this new composability, 'material' now shows how to
customize the logo and logo-splash beyond just replacing the png, but
now allowing the use of any type of image.  In this case, the
OpenStack logos have been upgraded with their svg counterparts.

It was also noted that the context selection menu was using the wrong
variable to set the color of its links. This was corrected.

Closes-Bug: #1518584

Change-Id: Ib544176b9836e17fc913170664646e1d10eda831
2016-01-14 20:44:37 -07:00
Rajat Vig
975550956d Set <base> element ONLY for Angular Panels
This patch add the <base> element only for
currently existing Angular Panels.

Change-Id: I230995c069d94ca41d70582b9c665289f0acb86e
Partially-Implements: blueprint angularize-images-table
2016-01-14 14:23:53 -08:00
Diana Whitten
b54f73d619 Branding: Horizon needs global footer override
Horizon footer is now configurable at a global level and can live
within a theme.

It was noted that the login page will usually want its own footer.

Change-Id: I61d7876b02cbff2d36b076c201847a39b0454136
Closes-bug: #1531335
2016-01-14 17:48:36 +00:00
Jenkins
eb2554ad9b Merge "Support javascript translation for plugin" 2016-01-14 15:16:05 +00:00
Rob Cresswell
716a6c6c3f Revert "Specify <base> element in all pages"
This reverts commit 4480055c9160e5773cf2de6edd7f9ff443608a0e.

This patch broke pagination from anything other than the first page, as
well as breaking links on the theme preview page.

Change-Id: I0d4398451601d702abbc33b2497e2c018a338cd9
2016-01-14 13:52:43 +00:00
Rajat Vig
4480055c91 Specify <base> element in all pages
This will allow $locationProvider to be set for the Horizon Application.
The new patch also makes all relatively scoped URI's
for Tabs and Pagination as absolute and fixes the
horizon legacy scripts to include the current window location.

Change-Id: I0ee517587e609856d7cafce519063b66878efb43
Closes-Bug: #1531737
2016-01-12 15:56:31 -08:00
Thai Tran
871505c130 Revert "Specify <base> element in all pages"
This reverts commit I8bad1cd274eec13ec805dc1701ddbf86cfd6e666.

This change is incomplete, and appears to break all lazy loaded tabs.
Since that will effectively freeze development, we should
revert the change until it is more complete.

Change-Id: I0e85f6eeba3fb2ba9f259d04f0b2a2e09137cd0e
Closes-Bug: #1533024
2016-01-11 18:09:45 -08:00
Thai Tran
b56d278582 Support javascript translation for plugin
It's not possible for plugins to contribute translations to the javascript
message catalog. Right now our files are hardcoded to allow only
contributions from horizon and openstack_dashboard. This patch fixes
the issue.

Change-Id: Idde2fc6ac0bf7f762a595cf139ed5184dad64540
Closes-Bug: #1523930
2016-01-11 13:46:05 -08:00
Rajat Vig
6698d26fba Set target to _self for Header Links
Angular Panels have enabled ngRoute and HTML5 which
allows Angular to handle routing which makes all
routes to pass via Angular.

Links that do not have route defintion as a result stop
working.
This can be fixed by specifying the target="_self" on the <a> tags.

This is already in place for the Sidebar. It needs to done for the
Header Links as well.

Closes-Bug: #1531734
Change-Id: Ida76b40fdf0b66ba3ee3af4d32701974c312af57
2016-01-08 19:51:03 +00:00
Rajat Vig
8f88604516 Specify <base> element in all pages
This will allow $locationProvider to be set for the
Horizon Application.

Closes-Bug: #1531737
Change-Id: I8bad1cd274eec13ec805dc1701ddbf86cfd6e666
2016-01-07 19:52:02 +00:00
Justin Pomeroy
0ce5c85f9e Escape RegEx characters from table quick search text
This fix escapes any regular expression characters from the table
quick search text so that the filter works as the user would
expect.

A new string utility module is created for the legacy javascript
string utils and this new function for escaping regex chars is
placed there along with the existing function for escaping HTML
chars.

Closes-Bug: #1327276
Change-Id: I514bbe7ae9e1f319ced693e9a3da2881a86d4f57
2015-11-27 17:24:43 +03:00
Timur Sufiev
dab7e45c74 Fix for the broken header's dropdowns for both v0.11.2 and v0.13
The dropdownToggle directive from angular-ui/bootstrap v0.11.2
conflicts with the native Bootstrap data-toggle="dropdown" attribute
(see https:github.com/angular-ui/bootstrap/issues/2156). This is fixed
in 0.13, but before that it'd be valuable to ensure that the same html
markup works the same way with both versions of angular-ui/bootstrap
(0.11.2 and 0.13). The decorator code could be safely deleted once
Horizon migrates to angular-ui/bootstrap v0.13.

Also fix dropdowns in material theme templates.

Closes-Bug: #1466146
Change-Id: I8f01c1ce7b0a5dc29bf9d8aba23c9ea7e6e1ec35
2015-11-26 18:00:26 +03:00
Shaoquan Chen
67beea4138 Enabling strict di mode
Using strict DI mode will improve angular's performance when injecting
dependencies in injectable functions because it doesn't have to
dynamically discover a function's dependencies.

It is suggested by Angular official web site to use strict dependency
injection mode in production for get better performance:
https://docs.angularjs.org/guide/di#using-strict-dependency-injection

This patch fixes all the places where explicit di is not applied, and
then enables strict-di mode in production.

This is prioritized as CRITICAL for Horizon Mitaka release:
https://etherpad.openstack.org/p/mitaka-horizon-priorities

Change-Id: I1c0e01d7ac9aec03d961f14ff7297bc98c513637
Implements: blueprint angular-performance-strict-di
2015-11-19 15:11:39 -08:00
Matt Borland
06e0335560 Add ngroute libraries to appropriate paths
This patch adds ngroute libraries to appropriate paths and creates an
ng_route_base block in the Django base so templates can insert routing
blocks, such as establishing a base path.

Implements: blueprint add-ngroute
Change-Id: I11dd3890d336f288e93bf884197e3f22dcd1da93
2015-11-19 07:25:13 -07:00
Rob Cresswell
f2b2289b4a Drop Django 1.7 support
Django 1.7 support ends in December 2015
(https://www.djangoproject.com/download/#supported-versions), so it
will not be supported by Mitaka release.

This patch removes many of the deprecation warnings:-

`RedirectView.permanent` change:
https://docs.djangoproject.com/en/1.8/ref/class-based-views/base/

`url` change:
https://docs.djangoproject.com/en/1.8/internals/deprecation/
See version 1.5 notes for the url and ssi template tag change

`django.forms.utils` change:
https://docs.djangoproject.com/en/1.8/internals/deprecation/
See version 1.9 notes

`firstof` change:
https://docs.djangoproject.com/en/1.8/internals/deprecation/
See version 1.8 notes for cycle and firstof template tags

Change-Id: If546c087e73d189daa92e5bd63f0533fcf19089f
Partially-Implements: blueprint drop-dj17
2015-11-16 11:55:38 +00:00
Diana Whitten
94464af154 Dropdowns should have a consistent design
While working on making various dropdowns in Horizon Bootstrap
compliant, it was noticed that there are two distinct types of
dropdowns currently being used in Horizon.  This creates some style
inconsitencies.  This consolidates the styles so that it will be
easier to make the component theme-ready.

Partially-Implements: blueprint horizon-theme-css-reorg

Change-Id: Ib2be3fef3af01fcf85163f24cabb835e23ca2aff
2015-11-05 21:40:12 -07:00
lin-hua-cheng
1f19522a85 Add optional Report Bug link to Horizon
Change-Id: I2d0053adbdfcb196683c709f755f778427e6109b
Closes-Bug: #1499476
2015-09-25 17:29:15 -07:00