django change font size and font family in tinymce - django

I have this script in my base file...
<script src="//tinymce.cachefly.net/4.1/tinymce.min.js"></script>
<script>
tinymce.init({selector:'textarea',
plugins: [
"advlist autolink lists link image charmap print preview hr anchor pagebreak",
"searchreplace wordcount visualblocks visualchars code fullscreen",
"insertdatetime media nonbreaking save table contextmenu directionality",
"emoticons template paste textcolor colorpicker textpattern"
],
});
</script>
In my tinymce text editor I see font of size I guess 10 px but I want to change it to 16 px and also want to change the font family..
Any help ??

To set default font size, just add the line
'content_style': '.mcecontentbody{font-size:13px;}', in TINYMCE_DEFAULT_CONFIG variable of settings.py
TINYMCE_DEFAULT_CONFIG = {
'theme': 'advanced',
'relative_urls': False,
'plugins': 'media,spellchecker',
'content_style': '.mcecontentbody{font-size:13px;}',
'theme_advanced_buttons1': 'bold,italic,underline,bullist,numlist,|,link,unlink,image',
'theme_advanced_resizing': True,
'theme_advanced_path': False,
}

Please see the settings.py config that worked for me:
TINYMCE_DEFAULT_CONFIG = {
'theme': "advanced", # default value
'relative_urls': False, # default value
'plugins': 'table,spellchecker,paste,searchreplace',
'theme_advanced_buttons1': 'bold,italic,underline,bullist,numlist,link,unlink,styleselect,fontselect,fontsizeselect',
'width': '100%',
'height': 300,
'paste_text_sticky': True,
'paste_text_sticky_default': True,
'valid_styles': 'font-weight,font-style,text-decoration',
'fontsize_formats': "8pt 10pt 11pt 12pt 13pt 14pt 16pt 18pt 20pt 24pt 36pt",
'font_formats': "Andale Mono=andale mono,times;" +
"Arial=arial,helvetica,sans-serif;" +
"Arial Black=arial black,avant garde;" +
"Book Antiqua=book antiqua,palatino;" +
"Comic Sans MS=comic sans ms,sans-serif;" +
"Courier New=courier new,courier;" +
"Georgia=georgia,palatino;" +
"Helvetica=helvetica;" +
"Impact=impact,chicago;" +
"Symbol=symbol;" +
"Tahoma=tahoma,arial,helvetica,sans-serif;" +
"Terminal=terminal,monaco;" +
"Times New Roman=times new roman,times;" +
"Trebuchet MS=trebuchet ms,geneva;" +
"Verdana=verdana,geneva;" +
"Webdings=webdings;" +
"Wingdings=wingdings,zapf dingbats",}
TINYMCE_SPELLCHECKER = True
TINYMCE_COMPRESSOR = True
Here is a screenshot:

Take a look at the tinyMCE configuration docs. There are two suitable settings for your needs: font_formats and fontsize_formats and are used like this:
tinymce.init({
fontsize_formats: "8pt 10pt 12pt 14pt 18pt 24pt 36pt",
font_formats: "Arial=arial,helvetica,sans-serif;"
});

Related

Shiny downloadHandler two image format downloading error

radioButtons(inputId = "var1", label = "Select type", choices = list("pdf", "png"))
I used above radio button for choosing format and followed code for download file
output$down <- downloadHandler(
filename = function() {
paste("The Plot", input$var1,sep=".")
},
content = function(file) {
if(input$var1 == "pdf")
pdf(file)
else
png(file)
print(vals$gg1)
dev.off() } )
But it generating only PDF and no png out. Is there any problem with my loop
Need to add cairo
png(file),type="cairo"

rmarkdown flexdashboard images folder deletes itself

My images folder deletes itself after first knitting of the document.
I've tried self_contained = TRUE in the YAML header, doesn't work.
(I'm not sure if this makes a difference but Shiny is embedded in the dashboard)
Below is my code :
---
title : app demo
author : yeshipants
output :
flexdashboard::flex_dashboard:
orientation: rows
self_contained : TRUE
source_code: embed
runtime: shiny
---
```{r setup}
knitr::opts_chunk$set(cache = FALSE)
```
```{r loadPackages, cache = TRUE}
setwd("C:/Users/user/Desktop/Training/OCR")
library(magick)
```
Column {.sidebar data-width=350}
-------------------------------------
### Input & Parameters
```{r inputImages, cache = TRUE}
selectInput("imagesToChoose",
label = "Choose an image to process",
choices = c("Language example 1",
"Language example 2",
"Jounal example"),
selected = "Language example 1")
```
Row {.tabset}
-------------------------------------
### Original Image
```{r displayImage, cache = FALSE}
renderImage({
if (input$imagesToChoose == "Language example 1"){
list(src = "images/receipt.png", height = 240, width = 300)
}
else if(input$imagesToChoose == "Language example 2"){
list(src = "images/french.JPG", height = 240, width = 300)
}
else if(input$imagesToChoose == "Jounal example"){
list(src = "images/journal.jpg", height = 240, width = 300)
}
})
```
I think you have to do:
renderImage({
......
}, deleteFile = FALSE)

Regular Expression to remove vendor prefixed CSS

Seems pretty simple, but with RegEx it seems nothing is simple. All I want to do is take an array of CSS properties like this:
[ 'background',
'border',
'font-size',
'margin',
'outline',
'padding',
'vertical-align',
'line-height',
'display',
'list-style',
'quotes',
'content',
'background-color',
'color',
'text-decoration',
'font-style',
'font-weight',
'border-bottom',
'cursor',
'border-collapse',
'border-spacing',
'border-top',
'height',
'box-shadow',
'text-shadow',
'page-break-inside',
'max-width',
'orphans',
'widows',
'page-break-after',
'font-family',
'src',
'speak',
'font-variant',
'text-transform',
'text-rendering',
'-webkit-font-smoothing',
'-moz-osx-font-smoothing',
'box-sizing',
'width',
'-webkit-tap-highlight-color',
'margin-top',
'margin-bottom',
'margin-left',
'touch-action',
'padding-top',
'padding-bottom',
'text-align',
'caption-side',
'border-radius',
'resize',
'min-width',
'-webkit-appearance',
'clear',
'margin-right',
'foat',
'float',
'position',
'overflow',
'clip',
'visibility',
'font',
'text-overflow',
'white-space',
'padding-left',
'padding-right',
'-ms-word-break',
'word-break',
'-webkit-hyphens',
'-moz-hyphens',
'-ms-hyphens',
'hyphens',
'pading-left',
'border-left',
'border-right',
'top',
'left',
'transform',
'-webkit-transform',
'z-index',
'right',
'bottom',
'max-height',
'isplay',
'overflow-x',
'flex-wrap',
'min-height',
'flex',
'order',
'align-items',
'align-self',
'background-image',
'opacity',
'background-size',
'background-repeat',
'data',
'user-select',
'border-color',
'transition',
'pointer-events',
'boder-color',
'border-top-right-radius',
'border-bottom-right-radius',
'border-top-left-radius',
'border-bottom-left-radius',
'border-width',
'border-style',
'-ms-transform' ]
And remove all of the vendor prefixed ones. What would the regular expression look like to only match strings like this -ms-transform and not strings like this border-width?
You can use Array#filter with RegExp#test.
var regex = /^-(webkit|moz|ms|o)-/;
var nonVendorPrefixedProperties = arr.filter(prop => !regex.test(prop));
The vendor prefixes
-webkit-: Chrome, newer versions of Opera
-moz-: Firefox
-o-: Old versions of Opera
-ms-: Internet Explorer
RegEx Explanation:
^: Start of line
-: Match hyphen
(webkit|moz|ms|o): Match vendor prefix webkit, moz, ms or o.
var arr = ['background',
'border',
'font-size',
'margin',
'outline',
'padding',
'vertical-align',
'line-height',
'display',
'list-style',
'quotes',
'content',
'background-color',
'color',
'text-decoration',
'font-style',
'font-weight',
'border-bottom',
'cursor',
'border-collapse',
'border-spacing',
'border-top',
'height',
'box-shadow',
'text-shadow',
'page-break-inside',
'max-width',
'orphans',
'widows',
'page-break-after',
'font-family',
'src',
'speak',
'font-variant',
'text-transform',
'text-rendering',
'-webkit-font-smoothing',
'-moz-osx-font-smoothing',
'box-sizing',
'width',
'-webkit-tap-highlight-color',
'margin-top',
'margin-bottom',
'margin-left',
'touch-action',
'padding-top',
'padding-bottom',
'text-align',
'caption-side',
'border-radius',
'resize',
'min-width',
'-webkit-appearance',
'clear',
'margin-right',
'foat',
'float',
'position',
'overflow',
'clip',
'visibility',
'font',
'text-overflow',
'white-space',
'padding-left',
'padding-right',
'-ms-word-break',
'word-break',
'-webkit-hyphens',
'-moz-hyphens',
'-ms-hyphens',
'hyphens',
'pading-left',
'border-left',
'border-right',
'top',
'left',
'transform',
'-webkit-transform',
'z-index',
'right',
'bottom',
'max-height',
'isplay',
'overflow-x',
'flex-wrap',
'min-height',
'flex',
'order',
'align-items',
'align-self',
'background-image',
'opacity',
'background-size',
'background-repeat',
'data',
'user-select',
'border-color',
'transition',
'pointer-events',
'boder-color',
'border-top-right-radius',
'border-bottom-right-radius',
'border-top-left-radius',
'border-bottom-left-radius',
'border-width',
'border-style',
'-ms-transform'
];
var regex = /^-(webkit|moz|ms)-/;
var nonVendorPrefixedProperties = arr.filter(prop => !regex.test(prop));
console.log(nonVendorPrefixedProperties);
document.getElementById('result').innerHTML = JSON.stringify(nonVendorPrefixedProperties, 0, 4);
<pre id="result"></pre>

How to configure simple links and image attaching in ckeditor?

I'm using django-ckeditor and I have some problems with the links and images.
Regarding Links:
In this interface you can see that this is not usable by the end users, as it is too complex and can lead to errors and security issues, as the button Browse Server literally permits the user browse uploaded content. What I want is something really simple: just an input text that automatically appends http (if not typed by user) and that opens the link in a new window aka target _blank.
I've tried to do so editing config.js with the following code. This has removed the Upload and Advanced tabs, removed unnecessary widgets from Info tab and made target _blank by default. But the Target tab is still present and the users can change it, as I apparently can't remove this tab, or else the default target is ignored I'm stuck with this. So, how can I set the target to _blank and remove the Target tab too? Is there a way to hide this tab, but not remove it?
CKEDITOR.on('dialogDefinition', function(ev) {
// Take the dialog name and its definition from the event data.
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
// Check if the definition is from the dialog we're
// interested in (the 'link' dialog).
if (dialogName == 'link') {
// Remove the 'Target', 'Upload' and 'Advanced' tabs from the 'Link' dialog.
// dialogDefinition.removeContents('target');
dialogDefinition.removeContents('upload');
dialogDefinition.removeContents('advanced');
// Get a reference to the 'Link Info' tab.
var infoTab = dialogDefinition.getContents('info');
// Remove unnecessary widgets from the 'Link Info' tab.
infoTab.remove('linkType');
infoTab.remove('protocol');
infoTab.remove('browse');
// Get a reference to the "Target" tab.
var targetTab = dialogDefinition.getContents('target');
// Set the default value for the URL field.
var targetField = targetTab.get('linkTargetType');
targetField['default'] = '_blank';
}
});
Regarding images:
There is a very similar situation: several tabs with too much options. What I need is something as easy as the option to attach images in Stackoverflow. Is there any free plugin that could allow me to add images through a link and by uploading them from the computer (with previsualization) using the ckeditor?
Thanks!
Finally I get simple dialogs for: including links, attaching images from a link or uploading from the computer and to include Youtube videos in a simple way. To do this I've edited the configuration file called config.js and it looks like this for my version CKeditor 4.1.2:
CKEDITOR.editorConfig = function( config ) {
// Define changes to default configuration here.
// For the complete reference:
// http://docs.ckeditor.com/#!/api/CKEDITOR.config
// Comment the following line in DEBUG mode:
config.removePlugins = 'devtools';
// See the most common block elements.
config.format_tags = 'p;h1;h2;h3;pre';
// Make dialogs simpler.
config.removeDialogTabs = 'image:advanced;image:Link;link:advanced;link:upload';
config.linkShowTargetTab = false;
// In CKEditor 4.1 or higher you need to disable ACF (Advanced Content Filter)
// to make Youtube plugin work:
config.allowedContent = true;
};
CKEDITOR.on('dialogDefinition', function(ev) {
// Take the dialog name and its definition from the event data.
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
// Check if the definition is from the dialog we're
// interested in (the 'link' dialog).
if (dialogName == 'link') {
// Remove the 'Upload' and 'Advanced' tabs from the 'Link' dialog.
// dialogDefinition.removeContents('upload');
// dialogDefinition.removeContents('advanced');
// Get a reference to the 'Link Info' tab.
var infoTab = dialogDefinition.getContents('info');
// Remove unnecessary widgets from the 'Link Info' tab.
infoTab.remove('linkType');
infoTab.remove('protocol');
infoTab.remove('browse');
// Get a reference to the "Target" tab and set default to '_blank'
var targetTab = dialogDefinition.getContents('target');
var targetField = targetTab.get('linkTargetType');
targetField['default'] = '_blank';
} else if (dialogName == 'image') {
// Remove the 'Link' and 'Advanced' tabs from the 'Image' dialog.
// dialogDefinition.removeContents('Link');
// dialogDefinition.removeContents('advanced');
// Get a reference to the 'Image Info' tab.
var infoTab = dialogDefinition.getContents('info');
// Remove unnecessary widgets/elements from the 'Image Info' tab.
infoTab.remove('browse');
infoTab.remove('txtHSpace');
infoTab.remove('txtVSpace');
infoTab.remove('txtBorder');
// infoTab.remove('cmbAlign');
}
});
To do this I've read a lot of documentation, but the best pages that have inpired me are the following:
http://ckeditor.com/ckeditor_4.1rc/samples/plugins/toolbar/toolbar.html
http://ckeditor.com/forums/Support/Removing-Tabs-Image-Dialog
http://ckeditor.com/forums/CKEditor/Complete-list-of-toolbar-items
http://khaledben.wordpress.com/2012/04/28/customize-ckeditor-dialog/
http://www.question2answer.org/qa/13255/simple-ckeditor-how-to-modify-it-to-be-simple-solution
I hope this helps someone else with the same problem. Cheers!
Here are a lot of tweaks I did for CKEditor v3.6.1 to make it usable (esp. image dialog and link dialog). They seem to work for CKEditor 4.x as well, just take what you need for your config.js:
CKEDITOR.editorConfig = function( config ) {
// Define changes to default configuration here. For example:
config.language = 'de';
config.extraPlugins = 'colordialog';
// config.extraPlugins = 'matheeditor';
// config.uiColor = '#AADC6E';
// config.image_previewText = CKEDITOR.tools.repeat('Custom lorem ipsum text here', 8 );
// config.contentsLanguage = 'de';
config.linkShowAdvancedTab = false;
config.linkShowTargetTab = false;
config.height = 350;
config.width = 680;
// change color palette
config.colorButton_colors = 'F00,11C11D,00F,B700B7,FF8C00,008080,808080,D3D3D3';
config.colorButton_enableMore = false;
// smaller editor-width for mobile devices
if (/iPhone|iPod/i.test(navigator.userAgent)) {
config.width = 300;
}
// for resizing the editor window
config.resize_minHeight = 350;
config.resize_maxHeight = 880;
config.resize_maxWidth = 910;
// remove all formatting from pasted text
config.forcePasteAsPlainText = true;
// remove font size, family, bg color from pasted text
config.pasteFromWordRemoveFontStyles = true;
// allow browser's spell checker
config.disableNativeSpellChecker = false;
// disable ckeditor context menu to allow native context menu (works on holding CTRL)
// open: http://stackoverflow.com/questions/2246631/how-to-disable-ckeditor-context-menu/12477378
// shortcuts for firefox and chrome (editor breaks if assigned in IE9)
// if(navigator.userAgent.toLowerCase().indexOf('firefox') > -1 || navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
if ( !(/MSIE (\d+\.\d+);/.test(navigator.userAgent)) ) {
config.keystrokes = [
// [ CKEDITOR.SHIFT + 45, 'pastefromword' ], //INS
[ CKEDITOR.CTRL + 76, 'link' ], //L
[ CKEDITOR.CTRL + CKEDITOR.ALT + 66, 'image' ], //B
[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 77, 'specialchar' ], //M
[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 188, 'subscript' ], //COMMA
[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 109, 'subscript' ], //-
[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 191, 'subscript' ], //#
[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 190, 'superscript' ], //PERIOD
[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 107, 'superscript' ], //+
[ CKEDITOR.CTRL + 66, 'bold' ], //B
[ CKEDITOR.CTRL + 73, 'italic' ], //I
[ CKEDITOR.CTRL + 85, 'underline' ], //U
[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 70, 'bold' ], //F
[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 75, 'italic' ], //K
[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 85, 'underline' ], //U
];
}
};
CKEDITOR.on( 'dialogDefinition', function( ev ) {
// take the dialog name and its definition from the event data
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
//var dialog = CKEDITOR.dialog.getCurrent();
//alert( dialog.getName() );
// check if the definition is from the dialog we are interested in (the 'link' dialog).
if(dialogName == 'link') {
dialogDefinition.onShow = function () {
var dialog = CKEDITOR.dialog.getCurrent();
//dialog.hidePage( 'target' ); // via config
//dialog.hidePage( 'advanced' ); // via config
elem = dialog.getContentElement('info','anchorOptions');
elem.getElement().hide();
elem = dialog.getContentElement('info','emailOptions');
elem.getElement().hide();
var elem = dialog.getContentElement('info','linkType');
elem.getElement().hide();
elem = dialog.getContentElement('info','protocol');
elem.disable();
};
}
else if(dialogName == 'image') {
// get a reference to the 'Link Info' tab.
var infoTab = dialogDefinition.getContents('info');
// remove unnecessary fields
infoTab.remove('ratioLock');
infoTab.remove('txtHeight');
infoTab.remove('txtWidth');
infoTab.remove('txtBorder');
infoTab.remove('txtHSpace');
infoTab.remove('txtVSpace');
infoTab.remove('cmbAlign');
//hide image preview (v2)
//field = infoTab.get( 'htmlPreview' );
//field.style = 'display:none';
// memo: dialogDefinition.onShow = ... throws JS error (C.preview not defined)
dialogDefinition.onLoad = function () {
var dialog = CKEDITOR.dialog.getCurrent();
// hide image preview
var elem = dialog.getContentElement('info','htmlPreview');
elem.getElement().hide();
// hide tabs and show only upload
dialog.hidePage('Link');
dialog.hidePage('advanced');
this.selectPage('Upload');
// hide url on start up, prevent user input external image URLs
// goes in onShow of image.js: dialog.hidePage('info');
// hide ok button so that upload button can only be used
// goes in onShow of image.js: document.getElementById(this.getButton('ok').domId).style.display='none';
// on tab switching or automatic after upload
this.on('selectPage', function (e) {
// show okay button of ckeditor dialog
document.getElementById(this.getButton('ok').domId).style.display='inline';
// after upload the selectPage is fired, show Bild-Info then
dialog.showPage( 'info' );
});
};
}
else if(dialogName == 'table') {
dialogDefinition.removeContents('advanced');
}
});
If you are using django-ckeditor, you can simply have the following configuration in the settings.py file. You can configure it to your needs. No need to mess with JS.
CKEDITOR_CONFIGS = {
'default': {
'toolbar': 'Custom',
'toolbar_Custom': [
['Bold', 'Italic', 'Underline', 'Strike'],
[
'NumberedList',
'BulletedList',
'Outdent',
'Indent',
'-',
'JustifyLeft',
'JustifyCenter',
'JustifyRight',
'JustifyBlock'
],
['Link', 'Unlink'],
['RemoveFormat', 'Source'],
],
'height': 300,
'width': 695,
'linkShowAdvancedTab': False,
'linkShowTargetTab': True,
},
}
Regarding links
Feel free to remove "Target" tab:
dialogDefinition.removeContents( 'target' );
Use the power of dataProcessor instead:
CKEDITOR.replace( 'editor1', {
on: {
instanceReady: function() {
this.dataProcessor.htmlFilter.addRules( {
elements: {
a: function( element ) {
element.attributes.target = '_blank';
}
}
});
}
}
} );
This will add target="_blank" to all <a> elements in editor output. See docs to know more.
Regarding images
There's nothing much beyond CKFinder (commercial), KCFinder, PDW File Browser and Jasfinder. At least I cannot recall any more.

Django: how to change the choices of AdminTimeWidget

The AdminTimeWidget rendered in admin for a DateTimeField displays an icon of a clock and when you click you have the choice between: "Now Midnight 6:00 Noon".
How can I change these choices to "16h 17h 18h"?
Chris has a great answer. As an alternative you could do this using just javascript. Place the following javascript on the pages where you want the different time options.
DateTimeShortcuts.overrideTimeOptions = function () {
// Find the first time element
timeElement = django.jQuery("ul.timelist li").eq(0).clone();
originalHref = timeElement.find('a').attr('href');
// remove all existing time elements
django.jQuery("ul.timelist li").remove();
// add new time elements representing those you want
var i=0;
for (i=0;i<=23;i++) {
// use a regular expression to update the link
newHref = originalHref.replace(/Date\([^\)]*\)/g, "Date(1970,1,1," + i + ",0,0,0)");
// update the text for the element
timeElement.find('a').attr('href', newHref).text(i+"h");
// Add the new element into the document
django.jQuery("ul.timelist").append(timeElement.clone());
}
}
addEvent(window, 'load', DateTimeShortcuts.overrideTimeOptions);
Subclass AdminTimeWidget to include a modified DateTimeShortcuts.js (get to that in a sec), then subclass AdminSplitDateTime to include your subclassed MyAdminTimeWidget instead of the default Django one:
from django.contrib.admin.widgets import AdminTimeWidget
from django.conf import settings
class MyAdminTimeWidget(AdminTimeWidget):
class Media:
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
settings.MEDIA_URL + "js/admin/DateTimeShortcuts.js")
class MyAdminSplitDateTime(AdminSplitDateTime):
def __init__(self, attrs=None):
widgets = [AdminDateWidget, MyAdminTimeWidget]
forms.MultiWidget.__init__(self, widgets, attrs)
The secret sauce is in django/contrib/admin/media/js/admin/DateTimeShortcuts.js. This is what creates the list you want to modify. Copy this file and paste it into your project's site_media/js/admin directory. The relevant code you need to modify is on lines 85-88:
quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + time_format + "'));");
Simply add to/delete from/modify that bit of javascript to your heart's content.
Finally, attach your new widget to any DateTimeFields you like. Your best bet for that will probably be the formfield_overrides attribute on ModelAdmin:
class MyModelAdmin(admin.ModelAdmin):
formfield_overrides = {
models.DateTimeField: {'widget': MyAdminSplitDateTime},
}
I tried using this method and found the above javascript didn't work when multiple datetime's were present on the form.
here is what I did.
In my ModelAdmin section i added:
class Media:
js = ('js/clock_time_selections.js',)
then in the js file:
$('document').ready(function () {
DateTimeShortcuts.overrideTimeOptions = function () {
var clockCount = 0;
console.log('ready');
$('ul.timelist').each(function () {
var $this = $(this);
var originalHref = $this.find('a').attr('href');
console.log(originalHref);
$this.find('li').remove();
for (i=8; i <= 20; i++) {
var newLink = '<li><a href="javascript:DateTimeShortcuts.handleClockQuicklink('+ clockCount + ', ' + i
+ ');"> ' + i + ':00h</a></li>';
$this.append(newLink);
}
//console.log($this.html());
clockCount++;
});
};
addEvent(window, 'load', DateTimeShortcuts.overrideTimeOptions);
});
Note: i had to put inside a document.ready because i found that i couldn't control where the script was included in the page (seems to have be loaded before the default calendar js files).
There's better solution. After reading DateTimeShortcuts.js the can be simplified to:
(function ($) {
$(document).ready(function () {
DateTimeShortcuts.clockHours.default_ = [];
for (let hour = 8; hour <= 20; hour++) {
let verbose_name = new Date(1970, 1, 1, hour, 0, 0).strftime('%H:%M');
DateTimeShortcuts.clockHours.default_.push([verbose_name, hour])
}
});
})(django.jQuery);
Then add this code to the javascript file in 'static//time-shortcuts.js' and add Meta to your admin model:
from django.contrib import admin
from .models import MyModel
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
class Media:
js = [
'<myapp>/time-shortcuts.js',
]
I went with a much simpler approach and it worked for me. I simply added choices to my model using the following code:
class Class(Model):
program = ForeignKey('Program')
time_of_the_day = TimeField(choices=(
(datetime.datetime.strptime('7:00 am', "%I:%M %p").time(), '7:00 am'),
(datetime.datetime.strptime('8:00 am', "%I:%M %p").time(), '8:00 am'),
(datetime.datetime.strptime('9:00 am', "%I:%M %p").time(), '9:00 am'),
(datetime.datetime.strptime('6:00 pm', "%I:%M %p").time(), '6:00 pm'),
(datetime.datetime.strptime('7:00 pm', "%I:%M %p").time(), '7:00 pm'),
(datetime.datetime.strptime('8:00 pm', "%I:%M %p").time(), '8:00 pm'),
(datetime.datetime.strptime('9:00 pm', "%I:%M %p").time(), '9:00 pm'),
))
Hope this helps
Overriding JS by DateTimeShortcuts.overrideTimeOptions function works only with one form
( bug: the change of time in child model affects parent model, so you can't change timefield in child model form by this widget)
If you want use custom time options with inlines:
in /static/admin/js/admin/DateTimeShortcuts.js
replace:
quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + time_format + "'));");
by:
for(j=6;j<=23;j++){
quickElement("a", quickElement("li", time_list, ""), j+":00", "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1," + j + ",0,0,0).strftime('" + time_format + "'));");
}
Expanding on #Bit68's answer, assuming other folks might want to create lists of regularly spaced times more than once, I created a helper function to build a choices tuple. (I'm adding a new answer because this length of code is too hard to follow in a comment.) This works in Django 2.2.
Note that this creates a dropdown list of options, it doesn't add options to the default admin date/time widget as the javascript methods do.
import datetime
def get_time_choices(start_time=datetime.time(9,0,0), end_time=datetime.time(17,0,0), delta=datetime.timedelta(minutes=15)):
'''
Builds a choices tuple of (time object, time string) tuples
starting at the start time specified and ending at or before
the end time specified in increments of size delta.
The default is to return a choices tuple for
9am to 5pm in 15-minute increments.
'''
time_choices = ()
time = start_time
while time <= end_time:
time_choices += ((time, time.strftime("%I:%M %p")),)
# This complicated line is because you can't add
# a timedelta object to a time object.
time = (datetime.datetime.combine(datetime.date.today(), time) + delta).time()
return time_choices
Then time_of_the_day = models.TimeField(choices=get_time_choices())