support for bootstrap4

This commit is contained in:
mdipierro
2017-11-13 23:29:34 -06:00
parent d424e5d317
commit 65b3e6dda9
17 changed files with 265 additions and 212 deletions
+4
View File
@@ -1,3 +1,7 @@
## 2.15.5
- pydal 17.11
- many bug fixes
## 2.15.1-4
- pydal 17.08
- dropped support for python 2.6
+8 -6
View File
@@ -30,11 +30,7 @@ update:
wget -O gluon/contrib/feedparser.py http://feedparser.googlecode.com/svn/trunk/feedparser/feedparser.py
wget -O gluon/contrib/simplejsonrpc.py http://rad2py.googlecode.com/hg/ide2py/simplejsonrpc.py
echo "remember that pymysql was tweaked"
src:
### Use semantic versioning
echo 'Version 2.15.4-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
### rm -f all junk files
make clean
rmfiles:
### clean up baisc apps
rm -f routes.py
rm -rf applications/*/sessions/*
@@ -46,6 +42,12 @@ src:
rm -rf applications/admin/uploads/*
rm -rf applications/welcome/uploads/*
rm -rf applications/examples/uploads/*
src:
### Use semantic versioning
echo 'Version 2.15.5-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
### rm -f all junk files
#make clean
# make rmfiles
### make welcome layout and appadmin the default
cp applications/welcome/views/appadmin.html applications/admin/views
cp applications/welcome/views/appadmin.html applications/examples/views
@@ -54,7 +56,7 @@ src:
### build web2py_src.zip
echo '' > NEWINSTALL
mv web2py_src.zip web2py_src_old.zip | echo 'no old'
cd ..; zip -r web2py/web2py_src.zip web2py/web2py.py web2py/anyserver.py web2py/fabfile.py web2py/gluon/* web2py/extras/* web2py/handlers/* web2py/examples/* web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/MANIFEST.in web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py
cd ..; zip -r --exclude=**.git** web2py/web2py_src.zip web2py/web2py.py web2py/anyserver.py web2py/fabfile.py web2py/gluon/* web2py/extras/* web2py/handlers/* web2py/examples/* web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/MANIFEST.in web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py
mdp:
make src
+1 -1
View File
@@ -1 +1 @@
Version 2.15.4-stable+timestamp.2017.09.01.22.38.25
Version 2.15.5-stable+timestamp.2017.11.13.21.46.11
File diff suppressed because one or more lines are too long
@@ -47,15 +47,3 @@ def download():
http://..../[app]/default/download/[filename]
"""
return response.download(request, db)
def call():
"""
exposes services. for example:
http://..../[app]/default/call/jsonrpc
decorate with @services.jsonrpc the functions to expose
supports xml, json, xmlrpc, jsonrpc, amfrpc, rss, csv
"""
return service()
+47 -25
View File
@@ -1,12 +1,19 @@
# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------
# AppConfig configuration made easy. Look inside private/appconfig.ini
# Auth is for authenticaiton and access control
# -------------------------------------------------------------------------
from gluon.contrib.appconfig import AppConfig
from gluon.tools import Auth
# -------------------------------------------------------------------------
# This scaffolding model makes your app work on Google App Engine too
# File is released under public domain and you can use without limitations
# -------------------------------------------------------------------------
if request.global_settings.web2py_version < "2.14.1":
raise HTTP(500, "Requires web2py 2.13.3 or newer")
if request.global_settings.web2py_version < "2.15.5":
raise HTTP(500, "Requires web2py 2.15.5 or newer")
# -------------------------------------------------------------------------
# if SSL/HTTPS is properly configured and you want all HTTP requests to
@@ -14,23 +21,18 @@ if request.global_settings.web2py_version < "2.14.1":
# -------------------------------------------------------------------------
# request.requires_https()
# -------------------------------------------------------------------------
# app configuration made easy. Look inside private/appconfig.ini
# -------------------------------------------------------------------------
from gluon.contrib.appconfig import AppConfig
# -------------------------------------------------------------------------
# once in production, remove reload=True to gain full speed
# -------------------------------------------------------------------------
myconf = AppConfig(reload=True)
configuration = AppConfig(reload=True)
if not request.env.web2py_runtime_gae:
# ---------------------------------------------------------------------
# if NOT running on Google App Engine use SQLite or other DB
# ---------------------------------------------------------------------
db = DAL(myconf.get('db.uri'),
pool_size=myconf.get('db.pool_size'),
migrate_enabled=myconf.get('db.migrate'),
db = DAL(configuration.get('db.uri'),
pool_size=configuration.get('db.pool_size'),
migrate_enabled=configuration.get('db.migrate'),
check_reserved=['all'])
else:
# ---------------------------------------------------------------------
@@ -52,12 +54,15 @@ else:
# by default give a view/generic.extension to all actions from localhost
# none otherwise. a pattern can be 'controller/function.extension'
# -------------------------------------------------------------------------
response.generic_patterns = ['*'] if request.is_local else []
response.generic_patterns = []
if request.is_local and not configuration.get('app.production'):
response.generic_patterns.append('*')
# -------------------------------------------------------------------------
# choose a style for forms
# -------------------------------------------------------------------------
response.formstyle = myconf.get('forms.formstyle') # or 'bootstrap3_stacked' or 'bootstrap2' or other
response.form_label_separator = myconf.get('forms.separator') or ''
response.formstyle = 'bootstrap4_inline'
response.form_label_separator = ''
# -------------------------------------------------------------------------
# (optional) optimize handling of static files
@@ -80,27 +85,24 @@ response.form_label_separator = myconf.get('forms.separator') or ''
# (more options discussed in gluon/tools.py)
# -------------------------------------------------------------------------
from gluon.tools import Auth, Service, PluginManager
# host names must be a list of allowed host names (glob syntax allowed)
auth = Auth(db, host_names=myconf.get('host.names'))
service = Service()
plugins = PluginManager()
auth = Auth(db, host_names=configuration.get('host.names'))
# -------------------------------------------------------------------------
# create all tables needed by auth if not custom tables
# create all tables needed by auth, maybe add a list of extra fields
# -------------------------------------------------------------------------
auth.settings.extra_fields['auth_user'] = []
auth.define_tables(username=False, signature=False)
# -------------------------------------------------------------------------
# configure email
# -------------------------------------------------------------------------
mail = auth.settings.mailer
mail.settings.server = 'logging' if request.is_local else myconf.get('smtp.server')
mail.settings.sender = myconf.get('smtp.sender')
mail.settings.login = myconf.get('smtp.login')
mail.settings.tls = myconf.get('smtp.tls') or False
mail.settings.ssl = myconf.get('smtp.ssl') or False
mail.settings.server = 'logging' if request.is_local else configuration.get('smtp.server')
mail.settings.sender = configuration.get('smtp.sender')
mail.settings.login = configuration.get('smtp.login')
mail.settings.tls = configuration.get('smtp.tls') or False
mail.settings.ssl = configuration.get('smtp.ssl') or False
# -------------------------------------------------------------------------
# configure auth policy
@@ -109,6 +111,26 @@ auth.settings.registration_requires_verification = False
auth.settings.registration_requires_approval = False
auth.settings.reset_password_requires_verification = True
# -------------------------------------------------------------------------
# read more at http://dev.w3.org/html5/markup/meta.name.html
# -------------------------------------------------------------------------
response.meta.author = configuration.get('app.author')
response.meta.description = configuration.get('app.description')
response.meta.keywords = configuration.get('app.keywords')
response.meta.generator = configuration.get('app.generator')
# -------------------------------------------------------------------------
# your http://google.com/analytics id
# -------------------------------------------------------------------------
response.google_analytics_id = configuration.get('google.analytics_id')
# -------------------------------------------------------------------------
# maybe use the scheduler
# -------------------------------------------------------------------------
if configuration.get('scheduler.enabled'):
from gluon.scheduler import Scheduler
scheduler = Scheduler(db, heartbeat=configure.get('heartbeat'))
# -------------------------------------------------------------------------
# Define your tables below (or better in another model file) for example
#
+17 -53
View File
@@ -1,29 +1,6 @@
# -*- coding: utf-8 -*-
# this file is released under public domain and you can use without limitations
# ----------------------------------------------------------------------------------------------------------------------
# Customize your APP title, subtitle and menus here
# ----------------------------------------------------------------------------------------------------------------------
response.logo = A(B('web', SPAN(2), 'py'), XML('&trade;&nbsp;'),
_class="navbar-brand", _href="http://www.web2py.com/",
_id="web2py-logo")
response.title = request.application.replace('_', ' ').title()
response.subtitle = ''
# ----------------------------------------------------------------------------------------------------------------------
# read more at http://dev.w3.org/html5/markup/meta.name.html
# ----------------------------------------------------------------------------------------------------------------------
response.meta.author = myconf.get('app.author')
response.meta.description = myconf.get('app.description')
response.meta.keywords = myconf.get('app.keywords')
response.meta.generator = myconf.get('app.generator')
# ----------------------------------------------------------------------------------------------------------------------
# your http://google.com/analytics id
# ----------------------------------------------------------------------------------------------------------------------
response.google_analytics_id = None
# ----------------------------------------------------------------------------------------------------------------------
# this is the main application menu add/remove items as required
# ----------------------------------------------------------------------------------------------------------------------
@@ -32,53 +9,42 @@ response.menu = [
(T('Home'), False, URL('default', 'index'), [])
]
DEVELOPMENT_MENU = True
# ----------------------------------------------------------------------------------------------------------------------
# provide shortcuts for development. remove in production
# provide shortcuts for development. you can remove everything below in production
# ----------------------------------------------------------------------------------------------------------------------
def _():
# ------------------------------------------------------------------------------------------------------------------
# shortcuts
# ------------------------------------------------------------------------------------------------------------------
app = request.application
ctr = request.controller
# ------------------------------------------------------------------------------------------------------------------
# useful links to internal and external resources
# ------------------------------------------------------------------------------------------------------------------
if not configuration.get('app.production'):
_app = request.application
response.menu += [
(T('My Sites'), False, URL('admin', 'default', 'site')),
(T('This App'), False, '#', [
(T('Design'), False, URL('admin', 'default', 'design/%s' % app)),
LI(_class="divider"),
(T('Design'), False, URL('admin', 'default', 'design/%s' % _app)),
(T('Controller'), False,
URL(
'admin', 'default', 'edit/%s/controllers/%s.py' % (app, ctr))),
'admin', 'default', 'edit/%s/controllers/%s.py' % (_app, request.controller))),
(T('View'), False,
URL(
'admin', 'default', 'edit/%s/views/%s' % (app, response.view))),
'admin', 'default', 'edit/%s/views/%s' % (_app, response.view))),
(T('DB Model'), False,
URL(
'admin', 'default', 'edit/%s/models/db.py' % app)),
'admin', 'default', 'edit/%s/models/db.py' % _app)),
(T('Menu Model'), False,
URL(
'admin', 'default', 'edit/%s/models/menu.py' % app)),
'admin', 'default', 'edit/%s/models/menu.py' % _app)),
(T('Config.ini'), False,
URL(
'admin', 'default', 'edit/%s/private/appconfig.ini' % app)),
'admin', 'default', 'edit/%s/private/appconfig.ini' % _app)),
(T('Layout'), False,
URL(
'admin', 'default', 'edit/%s/views/layout.html' % app)),
'admin', 'default', 'edit/%s/views/layout.html' % _app)),
(T('Stylesheet'), False,
URL(
'admin', 'default', 'edit/%s/static/css/web2py-bootstrap3.css' % app)),
(T('Database'), False, URL(app, 'appadmin', 'index')),
'admin', 'default', 'edit/%s/static/css/web2py-bootstrap3.css' % _app)),
(T('Database'), False, URL(_app, 'appadmin', 'index')),
(T('Errors'), False, URL(
'admin', 'default', 'errors/' + app)),
'admin', 'default', 'errors/' + _app)),
(T('About'), False, URL(
'admin', 'default', 'about/' + app)),
'admin', 'default', 'about/' + _app)),
]),
('web2py.com', False, '#', [
(T('Download'), False,
@@ -98,7 +64,6 @@ def _():
]),
(T('Documentation'), False, '#', [
(T('Online book'), False, 'http://www.web2py.com/book'),
LI(_class="divider"),
(T('Preface'), False,
'http://www.web2py.com/book/default/chapter/00'),
(T('Introduction'), False,
@@ -143,9 +108,8 @@ def _():
]),
]
if DEVELOPMENT_MENU:
_()
# ----------------------------------------------------------------------------------------------------------------------
# maybe add a wiki menu if app embeds wiki pages
# ----------------------------------------------------------------------------------------------------------------------
if "auth" in locals():
auth.wikimenu()
+7 -5
View File
@@ -5,6 +5,7 @@ author = Your Name <you@example.com>
description = a cool new app
keywords = web2py, python, framework
generator = Web2py Web Framework
production = false
; Host configuration
[host]
@@ -14,7 +15,6 @@ names = localhost:*, 127.0.0.1:*, *:*, *
[db]
uri = sqlite://storage.sqlite
migrate = true
; ignored for sqlite
pool_size = 10
; smtp address and credentials
@@ -25,7 +25,9 @@ login = username:password
tls = true
ssl = true
; form styling
[forms]
formstyle = bootstrap3_inline
separator =
[scheduler]
enabled = false
heartbeat = 1
[google]
analytics_id =
File diff suppressed because one or more lines are too long
@@ -108,7 +108,6 @@ select.autocomplete {
background: url(../images/background.jpg) no-repeat center center;
}
body {
padding-top: 60px;
margin-bottom: 60px;
}
header {
@@ -126,8 +125,7 @@ html {
bottom: 0;
width: 100%;
height: 60px;
background: #333;
color: #aaa;
background: #f7f7f7;
}
header h1 {
color: #FFF!important;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 724 KiB

File diff suppressed because one or more lines are too long
-6
View File
@@ -1,6 +0,0 @@
/*! Respond.js v1.4.2: min/max-width media query polyfill
* Copyright 2014 Scott Jehl
* Licensed under MIT
* http://j.mp/respondjs */
!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='&shy;<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){v(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},g=function(a){return a.replace(c.regex.minmaxwh,"").match(c.regex.other)};if(c.ajax=f,c.queue=d,c.unsupportedmq=g,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,comments:/\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,maxw:/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,minmaxwh:/\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,other:/\([^\)]*\)/g},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var h,i,j,k=a.document,l=k.documentElement,m=[],n=[],o=[],p={},q=30,r=k.getElementsByTagName("head")[0]||l,s=k.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=k.createElement("div"),c=k.body,d=l.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=k.createElement("body"),c.style.background="none"),l.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&l.insertBefore(c,l.firstChild),a=b.offsetWidth,f?l.removeChild(c):c.removeChild(b),l.style.fontSize=d,e&&(c.style.fontSize=e),a=j=parseFloat(a)},v=function(b){var c="clientWidth",d=l[c],e="CSS1Compat"===k.compatMode&&d||k.body[c]||d,f={},g=t[t.length-1],p=(new Date).getTime();if(b&&h&&q>p-h)return a.clearTimeout(i),i=a.setTimeout(v,q),void 0;h=p;for(var s in m)if(m.hasOwnProperty(s)){var w=m[s],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?j||u():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?j||u():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(n[w.rules]))}for(var C in o)o.hasOwnProperty(C)&&o[C]&&o[C].parentNode===r&&r.removeChild(o[C]);o.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=k.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,r.insertBefore(E,g.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(k.createTextNode(F)),o.push(E)}},w=function(a,b,d){var e=a.replace(c.regex.comments,"").replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},i=!f&&d;b.length&&(b+="/"),i&&(f=1);for(var j=0;f>j;j++){var k,l,o,p;i?(k=d,n.push(h(a))):(k=e[j].match(c.regex.findStyles)&&RegExp.$1,n.push(RegExp.$2&&h(RegExp.$2))),o=k.split(","),p=o.length;for(var q=0;p>q;q++)l=o[q],g(l)||m.push({media:l.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:n.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}v()},x=function(){if(d.length){var b=d.shift();f(b.href,function(c){w(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){x()},0)})}},y=function(){for(var b=0;b<t.length;b++){var c=t[b],e=c.href,f=c.media,g=c.rel&&"stylesheet"===c.rel.toLowerCase();e&&g&&!p[e]&&(c.styleSheet&&c.styleSheet.rawCssText?(w(c.styleSheet.rawCssText,e,f),p[e]=!0):(!/^([a-zA-Z:]*\/\/)/.test(e)&&!s||e.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&("//"===e.substring(0,2)&&(e=a.location.protocol+e),d.push({href:e,media:f})))}x()};y(),c.update=y,c.getEmValue=u,a.addEventListener?a.addEventListener("resize",b,!1):a.attachEvent&&a.attachEvent("onresize",b)}}(this);
+14 -26
View File
@@ -1,15 +1,9 @@
{{left_sidebar_enabled,right_sidebar_enabled=False,('message' in globals())}}
{{extend 'layout.html'}}
{{block header}}
<header class="container-fluid background">
<div class="jumbotron text-center">
{{if response.title:}}
<h1>{{=response.title}}
<small>{{=response.subtitle or ''}}</small></h1>
{{pass}}
</div>
</header>
<center style="background-color: #333; color:white; padding:30px">
<h1>/{{=request.application}}/{{=request.controller}}/{{=request.function}}
</center>
{{end}}
{{if 'message' in globals():}}
@@ -27,26 +21,20 @@
_href=URL('admin','default','peek',args=(request.application,'views',request.controller,'index.html')))))}}</li>
<li>{{=T('You can modify this application and adapt it to your needs')}}</li>
</ol>
<center style="padding:50px">
<a class="btn btn-primary" href="{{=URL('admin','default','index')}}">
<i class="glyphicon glyphicon-cog"></i>
{{=T("admin")}}
</a>
<a class="btn btn-secondary" href="{{=URL('examples','default','index')}}">{{=T("Online examples")}}</a>
<a class="btn btn-secondary" href="http://web2py.com">web2py.com</a>
<a class="btn btn-secondary" href="http://web2py.com/book">{{=T('Documentation')}}</a>
</center>
{{elif 'content' in globals():}}
{{=content}}
{{else:}}
{{=BEAUTIFY(response._vars)}}
{{pass}}
{{block right_sidebar}}
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title"><a class="btn-block"
href="{{=URL('admin','default','index')}}">
<i class="glyphicon glyphicon-cog"></i>
{{=T("admin")}}
</a></h3></div>
<div class="panel-body">
{{=T("Don't know what to do?")}}
</div>
<ul class="list-group">
<li class="list-group-item">{{=A(T("Online examples"), _href=URL('examples','default','index'))}}</li>
<li class="list-group-item"><a href="http://web2py.com">web2py.com</a></li>
<li class="list-group-item"><a href="http://web2py.com/book">{{=T('Documentation')}}</a></li>
</ul>
</div>
{{end}}
+50 -54
View File
@@ -21,84 +21,80 @@
<meta name="google-site-verification" content="">
<!-- include stylesheets -->
<link rel="stylesheet" href="{{=URL('static','css/bootstrap.min.css')}}"/>
<link rel="stylesheet" href="{{=URL('static','css/web2py-bootstrap3.css')}}"/>
<link rel="stylesheet" href="{{=URL('static','css/web2py-bootstrap4.css')}}"/>
<link rel="shortcut icon" href="{{=URL('static','images/favicon.ico')}}" type="image/x-icon">
<link rel="apple-touch-icon" href="{{=URL('static','images/favicon.png')}}">
<!-- All JavaScript at the bottom, except for Modernizr which enables
HTML5 elements & feature detects -->
<script src="{{=URL('static','js/modernizr-2.8.3.min.js')}}"></script>
<!--[if lt IE 9]>
<script src="{{=URL('static','js/respond-1.4.2.min.js')}}"></script>
<![endif]-->
<!-- Favicons -->
{{include 'web2py_ajax.html'}} <!-- this includes jquery.js, calendar.js/.css and web2py.js -->
{{block head}}{{end}}
{{
# using sidebars need to know what sidebar you want to use
mc0 = 'col-md-12'
mc1 = 'col-md-9'
mc2 = 'col-md-6'
left_sidebar_enabled = globals().get('left_sidebar_enabled', False)
right_sidebar_enabled = globals().get('right_sidebar_enabled', False)
middle_column = {0: mc0, 1: mc1, 2: mc2}[
(left_sidebar_enabled and 1 or 0)+(right_sidebar_enabled and 1 or 0)]
}}
</head>
<body>
<!--[if lt IE 8]><p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p><![endif]-->
<div class="w2p_flash alert alert-dismissable">{{=response.flash or ''}}</div>
<!-- Navbar ======================================= -->
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
{{=response.logo or ''}}
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav navbar-right">
{{='auth' in globals() and auth.navbar('Welcome',mode='dropdown') or ''}}
</ul>
{{if response.menu:}}
{{=MENU(response.menu, _class='nav navbar-nav',li_class='dropdown',ul_class='dropdown-menu')}}
<nav class="navbar navbar-toggleable-md navbar-light bg-faded">
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand" href="http://web2py.com">web2py</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav">
{{for _item in response.menu or []:}}
{{if len(_item)<4 or not _item[3]:}}
<li class="nav-item {{if _item[1]:}}active{{pass}}">
<a class="nav-link" href="{{=_item[2]}}">{{=_item[0]}}</a>
</li>
{{else:}}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="{{=_item[2]}}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{=_item[0]}}</a>
<div class="dropdown-menu">
{{for _subitem in _item[3]:}}
<a class="dropdown-item" href="{{=_subitem[2]}}">{{=_subitem[0]}}</a>
{{pass}}
</div>
</li>
{{pass}}
</div>
{{pass}}
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="text" placeholder="Search">
</form>
{{if 'auth' in globals():}}
<ul class="navbar-nav navbar-right">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{if auth.user:}}{{=auth.user.first_name}}{{else:}}LOGIN{{pass}}
</a>
<div class="dropdown-menu">
{{if auth.user:}}
<a class="dropdown-item" href="{{=URL('default','user/profile')}}">{{=T('Profile')}}</a>
<a class="dropdown-item" href="{{=URL('default','user/change_password')}}">{{=T('Change Password')}}</a>
<a class="dropdown-item" href="{{=URL('default','user/logout')}}">{{=T('Logout')}}</a>
{{else:}}
<a class="dropdown-item" href="{{=URL('default','user/login')}}">{{=T('Login')}}</a>
<a class="dropdown-item" href="{{=URL('default','user/register')}}">{{=T('Sign up')}}</a>
<a class="dropdown-item" href="{{=URL('default','user/request_password')}}">{{=T('Lost Password')}}</a>
{{pass}}
</div>
</li>
</ul>
{{pass}}
</div>
</nav>
<!-- Masthead ===================================== -->
{{block header}}
{{end}}
<!-- Main ========================================= -->
<!-- Begin page content -->
<div class="container-fluid main-container">
{{if left_sidebar_enabled:}}
<div class="col-md-3 left-sidebar">
{{block left_sidebar}}
<h3>Left Sidebar</h3>
<p></p>
{{end}}
</div>
{{pass}}
<div class="{{=middle_column}}">
<div class="col-md-12">
{{block center}}
{{include}}
{{end}}
</div>
{{if right_sidebar_enabled:}}
<div class="col-md-3">
{{block right_sidebar}}
<h3>Right Sidebar</h3>
<p></p>
{{end}}
</div>
{{pass}}
</div>
{{block footer}} <!-- this is default footer -->
@@ -114,7 +110,7 @@
{{end}}
<!-- The javascript =============================== -->
<script src="{{=URL('static','js/bootstrap.min.js')}}"></script>
<script src="{{=URL('static','js/web2py-bootstrap3.js')}}"></script>
<script src="{{=URL('static','js/web2py-bootstrap4.js')}}"></script>
{{block page_js}}{{end page_js}}
{{if response.google_analytics_id:}}
<!-- Analytics ==================================== -->
+102
View File
@@ -1088,6 +1088,106 @@ def formstyle_bootstrap3_inline_factory(col_label_size=3):
return parent
return _inner
# bootstrap 4
def formstyle_bootstrap4_stacked(form, fields):
""" bootstrap 3 format form layout
Note:
Experimental!
"""
parent = CAT()
for id, label, controls, help in fields:
# wrappers
_help = SPAN(help, _class='help-block')
# embed _help into _controls
_controls = CAT(controls, _help)
if isinstance(controls, INPUT):
if controls['_type'] == 'submit':
controls.add_class('btn btn-primary')
if controls['_type'] == 'button':
controls.add_class('btn btn-secondary')
elif controls['_type'] == 'file':
controls.add_class('input-file')
elif controls['_type'] in ('text', 'password'):
controls.add_class('form-control')
elif controls['_type'] == 'checkbox':
label['_for'] = None
label.insert(0, controls)
label.insert(0, ' ')
_controls = DIV(label, _help, _class="checkbox")
label = ''
elif isinstance(controls, (SELECT, TEXTAREA)):
controls.add_class('form-control')
elif isinstance(controls, SPAN):
_controls = P(controls.components)
elif isinstance(controls, UL):
for e in controls.elements("input"):
e.add_class('form-control')
elif isinstance(controls, CAT) and isinstance(controls[0], INPUT):
controls[0].add_class('form-control')
if isinstance(label, LABEL):
label['_class'] = add_class(label.get('_class'), 'form-control-label')
parent.append(DIV(label, _controls, _class='form-group', _id=id))
return parent
def formstyle_bootstrap4_inline_factory(col_label_size=3):
""" bootstrap 3 horizontal form layout
Note:
Experimental!
"""
def _inner(form, fields):
form.add_class('form-horizontal')
label_col_class = "col-sm-%d" % col_label_size
col_class = "col-sm-%d" % (12 - col_label_size)
offset_class = "col-sm-offset-%d" % col_label_size
parent = CAT()
for id, label, controls, help in fields:
# wrappers
_help = SPAN(help, _class='help-block')
# embed _help into _controls
_controls = DIV(controls, _help, _class="%s" % (col_class))
if isinstance(controls, INPUT):
if controls['_type'] == 'submit':
controls.add_class('btn btn-primary')
_controls = DIV(controls, _class="%s %s" % (col_class, offset_class))
if controls['_type'] == 'button':
controls.add_class('btn btn-secondary')
elif controls['_type'] == 'file':
controls.add_class('input-file')
elif controls['_type'] in ('text', 'password'):
controls.add_class('form-control')
elif controls['_type'] == 'checkbox':
label['_for'] = None
label.insert(0, controls)
label.insert(1, ' ')
_controls = DIV(DIV(label, _help, _class="checkbox"),
_class="%s %s" % (offset_class, col_class))
label = ''
elif isinstance(controls, (SELECT, TEXTAREA)):
controls.add_class('form-control')
elif isinstance(controls, SPAN):
_controls = P(controls.components,
_class="form-control-static %s" % col_class)
elif isinstance(controls, UL):
for e in controls.elements("input"):
e.add_class('form-control')
elif isinstance(controls, CAT) and isinstance(controls[0], INPUT):
controls[0].add_class('form-control')
if isinstance(label, LABEL):
label['_class'] = add_class(label.get('_class'), 'form-control-label %s' % label_col_class)
parent.append(DIV(label, _controls, _class='form-group', _id=id))
return parent
return _inner
class SQLFORM(FORM):
@@ -1176,6 +1276,8 @@ class SQLFORM(FORM):
bootstrap=formstyle_bootstrap,
bootstrap3_stacked=formstyle_bootstrap3_stacked,
bootstrap3_inline=formstyle_bootstrap3_inline_factory(3),
bootstrap4_stacked=formstyle_bootstrap4_stacked,
bootstrap4_inline=formstyle_bootstrap4_inline_factory(3),
inline=formstyle_inline,
)