Skip to content

Commit

Permalink
Merge branch 'release-1.5.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
abidibo committed May 13, 2019
2 parents e791b5d + a891822 commit 65f1e5a
Show file tree
Hide file tree
Showing 25 changed files with 352 additions and 38 deletions.
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Documentation: [readthedocs](http://django-baton.readthedocs.io/)
- [Configuration](#configuration)
- [Menu](#configuration-menu)
- [Analytics](#configuration-analytics)
- [Signals](#signals)
- [Text Input Filters](#text-input-filters)
- [Form Tabs](#form-tabs)
- [Customization](#customization)
Expand All @@ -33,6 +34,7 @@ Everything is styled through css, and when an help is needed, js is armed.
- Custom and flexible sidebar menu
- Text input filters facility
- Form tabs out of the box
- Lazy load of current uploaded images
- Optional index page filled with google analytics widgets
- Customization available recompiling the js app provided
- it translations provided
Expand Down Expand Up @@ -149,6 +151,10 @@ The configuration dictionary must be defined inside your settings:
{ 'type': 'title', 'label': 'Contents', 'apps': ('flatpages', ) },
{ 'type': 'model', 'label': 'Pages', 'name': 'flatpage', 'app': 'flatpages' },
{ 'type': 'free', 'label': 'Custom Link', 'url': 'http://www.google.it', 'perms': ('flatpages.add_flatpage', 'auth.change_user') },
{ 'type': 'free', 'label': 'My parent voice', 'children': [
{ 'type': 'model', 'label': 'A Model', 'name': 'mymodelname', 'app': 'myapp' },
{ 'type': 'free', 'label': 'Another custom link', 'url': 'http://www.google.it' },
] },
),
'ANALYTICS': {
'CREDENTIALS': os.path.join(BASE_DIR, 'credentials.json'),
Expand All @@ -169,6 +175,11 @@ Let's see the `MENU` and `ANALYTICS` configurations in detail.

Currently four kind of voices are supported: _title_, _app_, _model_ and _free_.

Title and free voices can have children, children follow the following rules:

- children voices icons are ignored
- children voices children are ignored (do not place an app voice as child)

First of all, if you don't define a MENU key in the configuration dictionary, the default MENU is shown.
If you define a MENU key, then the custom menu is built and shown.

Expand Down Expand Up @@ -211,6 +222,41 @@ Once the service account is created, you can click the Generate New JSON Key but

Add the service account as a user in Google Analytics. The service account you created in the previous step has an email address that you can add to any of the Google Analytics views you'd like to request data from. It's generally best to only grant the service account read-only access.

## <a name="signals"></a>Signals

Baton provides a dispatcher that can be used to register function that will be called when some events occurr.
At this moment Baton emits four types of events:

- `onNavbarReady`: dispatched when the navbar is fully rendered
- `onMenuReady`: dispatched when the menu is fully rendered (probably the last event fired, since the menu contents are retrieves async)
- `onMenuError`: dispatched if the request sent to retrieve menu contents fails
- `onReady`: dispatched when Baton js has finished its sync job

In order to use them just override the baton `admin/base_site.html` template and register your listeners **before** calling `Baton.init`, i.e.

<!-- ... -->
<script>
{% baton_config 'CONFIRM_UNSAVED_CHANGES' as confirm_unsaved_changes %}
{% baton_config 'SHOW_MULTIPART_UPLOADING' as show_multipart_uploading %}
(function ($, undefined) {
$(window).on('load', function () {
// init listeners
Baton.Dispatcher.register('onReady', function () { console.log('BATON IS READY') })
Baton.Dispatcher.register('onMenuReady', function () { console.log('BATON MENU IS READY') })
Baton.Dispatcher.register('onNavbarReady', function () { console.log('BATON NAVBAR IS READY') })
// end listeners
Baton.init({
api: {
app_list: '{% url 'baton-app-list-json' %}'
},
confirmUnsavedChanges: {% if confirm_unsaved_changes %}true{% else%}false{% endif %},
showMultipartUploading: {% if show_multipart_uploading %}true{% else%}false{% endif %}
});
})
})(jQuery, undefined)
</script>
<!-- ... -->

## <a name="text-input-filters"></a>Text Input Filters

Taken from this [medium article](https://medium.com/@hakibenita/how-to-add-a-text-filter-to-django-admin-5d1db93772d8)
Expand Down
28 changes: 14 additions & 14 deletions baton/static/baton/app/dist/baton.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion baton/static/baton/app/dist/baton.min.js.map

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion baton/static/baton/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "baton",
"version": "1.4.0",
"version": "1.5.0",
"description": "Django Baton App",
"main": "index.js",
"scripts": {
Expand All @@ -22,6 +22,7 @@
"css-loader": "^0.26.1",
"file-loader": "^0.10.0",
"jquery": "^3.1.1",
"js-event-dispatcher": "^0.1.0",
"node-sass": "^4.5.0",
"popper.js": "^1.14.4",
"postcss-loader": "^3.0.0",
Expand Down
22 changes: 21 additions & 1 deletion baton/static/baton/app/src/core/ChangeForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ let ChangeForm = {
this.spinner()
}
this.fixWrappedFields()
this.lazyLoadImages()
},
activate: function () {
this.form.on('submit', () => (this.formSubmitting = true))
Expand Down Expand Up @@ -69,7 +70,7 @@ let ChangeForm = {
})
if (run) {
let overlay = $('<div />', {'class': 'spinner-overlay'}).appendTo(document.body)
let spinner = $('<i />', {'class': 'fa fa-circle-o-notch fa-spin fa-3x fa-fw'})
let spinner = $('<i />', {'class': 'fa fa-spinner fa-spin fa-3x fa-fw'})
$('<div />').append(
$('<p />').text(this.t.get('uploading')),
spinner
Expand All @@ -79,6 +80,25 @@ let ChangeForm = {
fixWrappedFields: function () {
this.form.find('.form-row > .fieldBox').wrapAll('<div class="wrapped-fields-container" />')
this.form.find('.wrapped-fields-container > .fieldBox').children().unwrap()
},
lazyLoadImages: function () {
$('.file-upload').each(function (index, p) {
let cur = $(p).find('a')
if (cur.length) {
let url = cur.attr('href')
let ext = url.split('.').pop()
if (['jpg', 'jpeg', 'png', 'bmp', 'svg', 'gif', 'tif'].indexOf(ext) !== -1) {
let spinner = $('<i />', {'class': 'fa fa-spinner fa-spin fa-2x fa-fw'}).css('color', '#aaa')
let preview = $('<div />', {'class': 'py-2'}).append(spinner)
$(p).prepend(preview)
let image = new Image()
image.onload = function () {
spinner.replaceWith($(image).addClass('baton-image-preview'))
}
image.src = url
}
}
})
}
}

Expand Down
26 changes: 26 additions & 0 deletions baton/static/baton/app/src/core/Filer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import $ from 'jquery'
import { copyTextToClipboard } from 'core/Utils'

let Filer = {
/**
* ChangeList component
*
* Filtering stuff
*/
init: function (opts) {
this.fixIcons()
this.fixCopyToClipboard()
},
fixIcons: function () {
$('.fa-pencil').addClass('fa-pencil-alt')
},
fixCopyToClipboard: function () {
let copyBtns = $('.action-button .fa-link')
copyBtns.on('click', function (evt) {
evt.preventDefault()
copyTextToClipboard($(this).parent('.action-button').next('.action-button').attr('href'))
})
}
}

export default Filer
5 changes: 4 additions & 1 deletion baton/static/baton/app/src/core/Menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ let Menu = {
*
* Adds a sidebar menu to the document
*/
init: function (config) {
init: function (config, Dispatcher) {
this.Dispatcher = Dispatcher
this.appListUrl = config.api.app_list
this.fixNodes()
this.fetchData()
Expand Down Expand Up @@ -34,12 +35,14 @@ let Menu = {
let self = this
$.getJSON(this.appListUrl, function (data) {
self.render(data)
self.Dispatcher.emit('onMenuReady')
})
.fail(function (err) {
console.error(err.responseText)
self.menu.remove()
$('#content').removeClass('col-md-9').removeClass('col-lg-10')
.css('flex-grow', 1)
self.Dispatcher.emit('onMenuError')
})
},
setHeight: function () {
Expand Down
15 changes: 12 additions & 3 deletions baton/static/baton/app/src/core/Tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ let Tabs = {
this.main.attr('data-baton-tab', 'main')
this.createNav()
this.createPanes()
this.showErrors()
this.checkHash()
this.showErrors()
}
},
shouldRun: function () {
Expand All @@ -34,7 +34,9 @@ let Tabs = {
'class': 'nav-link active',
'data-toggle': 'tab',
href: '#main'
}).text(this.main.children('h2').hide().text()))
}).text(this.main.children('h2').hide().text()).on('click', function () {
location.hash = $(this).attr('href')
}))
.appendTo(this.nav)

this.tabsEl.forEach((el) => {
Expand Down Expand Up @@ -64,11 +66,18 @@ let Tabs = {
'class': 'nav-link',
'data-toggle': 'tab',
href: '#' + el
}).text(domEl.find('h2:first-child').first().hide().text()))
}).text(domEl.find('h2:first-child').first().hide().text()).on('click', function () {
location.hash = $(this).attr('href')
}))
.appendTo(this.nav)
})

this.main.before(this.nav)

// do not preserve hash if pressing save and add another
$('input[name="_addanother"]').on('click', function () {
location.hash = ''
})
},
createInlineEl: function (el, setDataTab = false) {
let domEl
Expand Down
61 changes: 61 additions & 0 deletions baton/static/baton/app/src/core/Utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import Translator from 'core/i18n'

export function copyTextToClipboard (text) {
let t = new Translator($('html').attr('lang'))
var textArea = document.createElement('textarea')

//
// *** This styling is an extra step which is likely not required. ***
//
// Why is it here? To ensure:
// 1. the element is able to have focus and selection.
// 2. if element was to flash render it has minimal visual impact.
// 3. less flakyness with selection and copying which **might** occur if
// the textarea element is not visible.
//
// The likelihood is the element won't even render, not even a flash,
// so some of these are just precautions. However in IE the element
// is visible whilst the popup box asking the user for permission for
// the web page to copy to the clipboard.
//

// Place in top-left corner of screen regardless of scroll position.
textArea.style.position = 'fixed'
textArea.style.top = 0
textArea.style.left = 0

// Ensure it has a small width and height. Setting to 1px / 1em
// doesn't work as this gives a negative w/h on some browsers.
textArea.style.width = '2em'
textArea.style.height = '2em'

// We don't need padding, reducing the size if it does flash render.
textArea.style.padding = 0

// Clean up any borders.
textArea.style.border = 'none'
textArea.style.outline = 'none'
textArea.style.boxShadow = 'none'

// Avoid flash of white box if rendered for any reason.
textArea.style.background = 'transparent'

textArea.value = text

document.body.appendChild(textArea)

textArea.select()

try {
var successful = document.execCommand('copy')
var msg = successful ? 'successful' : 'unsuccessful'
console.log('Copying text command was ' + msg)
} catch (err) {
window.prompt(
t.get('cannotCopyToClipboardMessage'),
text
)
}

document.body.removeChild(textArea)
}
4 changes: 4 additions & 0 deletions baton/static/baton/app/src/core/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export const messages = {
filter: {
en: 'Filter',
it: 'Filtra'
},
cannotCopyToClipboardMessage: {
en: 'Cannot copy to clipboard, please do it manually: Ctrl+C, Enter',
it: 'Impossibile copiare negli appunti, copiare manualmente: Ctrl+C, Enter'
}
}

Expand Down
13 changes: 11 additions & 2 deletions baton/static/baton/app/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import 'bootstrap/dist/js/bootstrap'
import './styles/baton.scss'

import Dispatcher from 'js-event-dispatcher/dist/EventDispatcher'
import Navbar from 'core/Navbar'
import Footer from 'core/Footer'
import Menu from 'core/Menu'
Expand All @@ -11,6 +12,7 @@ import Analytics from 'core/Analytics'
import Tabs from 'core/Tabs'
import ChangeList from 'core/ChangeList'
import ChangeForm from 'core/ChangeForm'
import Filer from 'core/Filer'

window.Baton = {
intialized: false,
Expand All @@ -20,8 +22,9 @@ window.Baton = {
let page = this.page()

Navbar.init()
Dispatcher.emit('onNavbarReady')
if (page !== 'login' && !/_popup/.test(location.search)) {
Menu.init(config)
Menu.init(config, Dispatcher)
}
if (page === 'logout' || page === 'password_change_success') {
ActionResult.init()
Expand All @@ -34,6 +37,8 @@ window.Baton = {
confirmUnsavedChanges: config.confirmUnsavedChanges,
showMultipartUploading: config.showMultipartUploading
})
} else if (page === 'filer') {
Filer.init()
}
Footer.init({
remove: /_popup/.test(location.search)
Expand All @@ -45,6 +50,7 @@ window.Baton = {
}
console.info('Baton:', 'ready')
document.body.className += ' baton-ready'
Dispatcher.emit('onReady')
},
page: function () {
if (/^(\/[a-z]{2})?\/admin\/$/.test(location.pathname)) {
Expand All @@ -63,8 +69,11 @@ window.Baton = {
return 'change_form'
} else if (document.getElementById('changelist')) {
return 'changelist'
} else if (/\/filer\//.test(location.pathname)) {
return 'filer'
}
},
Analytics: Analytics
Analytics: Analytics,
Dispatcher: Dispatcher
}
window.jQuery = jQuery
8 changes: 8 additions & 0 deletions baton/static/baton/app/src/styles/_changeform.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@
}
}
}

.baton-image-preview {
height: 100px;

&:hover {
box-shadow: 0 0 10px #aaa;
}
}
}

.spinner-overlay {
Expand Down
13 changes: 13 additions & 0 deletions baton/static/baton/app/src/styles/_filer.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
.filter-files-field.js-filter-files {
width: 170px;
}

.filter-files-container .filter-search-wrapper {
width: auto !important;
}

.filebrowser {
.paginator {
span {
border: 0 !important;
width: auto !important;
}
}
}

0 comments on commit 65f1e5a

Please sign in to comment.