Merge pull request #318 from ilvalle/todolist

Todolist panel in admin editor
This commit is contained in:
mdipierro
2013-12-06 18:28:18 -08:00
7 changed files with 148 additions and 36 deletions
+30
View File
@@ -750,6 +750,7 @@ def edit():
return response.json({'file_hash': file_hash, 'saved_on': saved_on, 'functions': functions, 'controller': controller, 'application': request.args[0], 'highlight': highlight}) return response.json({'file_hash': file_hash, 'saved_on': saved_on, 'functions': functions, 'controller': controller, 'application': request.args[0], 'highlight': highlight})
else: else:
file_details = dict(app=request.args[0], file_details = dict(app=request.args[0],
lineno=request.vars.lineno or 1,
editor_settings=preferences, editor_settings=preferences,
filename=filename, filename=filename,
realfilename=realfilename, realfilename=realfilename,
@@ -773,6 +774,35 @@ def edit():
else: else:
return response.json(file_details) return response.json(file_details)
def todolist():
""" Returns all TODO of the requested app
"""
app = request.vars.app or ''
app_path = apath('%(app)s' % {'app':app}, r=request)
dirs=['models', 'controllers', 'modules', 'private' ]
def listfiles(app, dir, regexp='.*\.py$'):
files = sorted( listdir(apath('%(app)s/%(dir)s/' % {'app':app, 'dir':dir}, r=request), regexp))
files = [x.replace(os.path.sep, '/') for x in files if not x.endswith('.bak')]
return files
pattern = '#\s*(todo)+\s+(.*)'
regex = re.compile(pattern, re.IGNORECASE)
output = []
for d in dirs:
for f in listfiles(app, d):
matches = []
filename= apath(os.path.join(app, d, f), r=request)
with open(filename, 'r') as f_s:
src = f_s.read()
for m in regex.finditer(src):
start = m.start()
lineno = src.count('\n', 0, start) + 1
matches.append({'text':m.group(0), 'lineno':lineno})
if len(matches) != 0:
output.append({'filename':f,'matches':matches, 'dir':d})
return {'todo':output, 'app': app}
def resolve(): def resolve():
""" """
@@ -1,7 +1,13 @@
/* TODO rename this file as web2py-editor.css */
/* Fullscreen */ /* Fullscreen */
.CodeMirror-fullscreen { .CodeMirror-fullscreen {
z-index: 1030; z-index: 1030;
} }
.CodeMirror {
border-top: 1px solid #ddd;
/*border-left: 1px solid #ddd;*/
border-bottom: 1px solid #ddd;
}
/* BREAKPOINTS */ /* BREAKPOINTS */
@@ -36,3 +42,15 @@
/*.nav-tabs>li { /*.nav-tabs>li {
min-width: 100px; min-width: 100px;
}*/ }*/
#windows_divs > div {
position: fixed;
height: 30%;
left: 0;
background: white;
right: 0;
bottom: 41px;
z-index: 1030;
overflow: inherit;
border-top: 1px solid #ddd;
}
+4 -4
View File
@@ -248,7 +248,7 @@ function keepalive(url) {
}); });
} }
function load_file(url) { function load_file(url, lineno) {
$.ajax({ $.ajax({
type: "GET", type: "GET",
contentType: 'application/json', contentType: 'application/json',
@@ -263,13 +263,13 @@ function load_file(url) {
var tab_header = '<li><a title="'+ json['filename'] +'" href="#' + json['id'] + '" data-toggle="tab"><button type="button" class="close">&times;</button>' + json['realfilename'] + '</a></li>'; var tab_header = '<li><a title="'+ json['filename'] +'" href="#' + json['id'] + '" data-toggle="tab"><button type="button" class="close">&times;</button>' + json['realfilename'] + '</a></li>';
var tab_body = '<div id="' + json['id'] + '" class="tab-pane fade in " >' + json['plain_html'] + '</div>'; var tab_body = '<div id="' + json['id'] + '" class="tab-pane fade in " >' + json['plain_html'] + '</div>';
if(json['force'] === false) { if(json['force'] === false) {
$('#filesTab').append($(tab_header)); $('#myTabContent').append($(tab_body)); // First load the body
$('#myTabContent').append($(tab_body)); $('#filesTab').append($(tab_header)); // Then load the header which trigger the shown event
} else { } else {
$('#' + json['id']).html($(tab_body)); $('#' + json['id']).html($(tab_body));
} }
} }
$("a[href='#" + json['id'] + "']").click(); $("a[href='#" + json['id'] + "']").trigger('click', lineno);
} }
}, },
error: function (x) { error: function (x) {
+38 -4
View File
@@ -52,12 +52,17 @@
<link rel="stylesheet" href="{{=css_url}}/web2py-codemirror.css"> <link rel="stylesheet" href="{{=css_url}}/web2py-codemirror.css">
<script type="text/javascript"> <script type="text/javascript">
var current_font_incr = 0; // Default font-size, 0 means isn't set var current_font_incr = 0; // Default font-size, 0 means isn't set
$(document).on('shown click', 'a[data-toggle="tab"]', function (e) { $(document).on('shown click', 'a[data-toggle="tab"]', function (e, lineno) {
var tab_id = $(this).attr('href'); var tab_id = $(this).attr('href');
var editor = $(tab_id + " textarea").data('editor'); var editor = $(tab_id + " textarea").data('editor');
if (editor) { if (editor) {
editor.setSize('100%', $(tab_id).height()); editor_height = $(window).height() - $(tab_id + " .well-small").offset().top - $(tab_id + " .well-small").outerHeight(true) - $('.navbar-fixed-bottom').outerHeight() - 1;
editor.setSize('100%', editor_height);
editor.refresh(); editor.refresh();
if (lineno !== undefined) {
editor.setCursor(lineno);
editor.centerOnCursor();
}
} }
var n_li = $('#filesTab li').length; var n_li = $('#filesTab li').length;
$.each($('#filesTab li'), function(index, element) { $.each($('#filesTab li'), function(index, element) {
@@ -104,7 +109,8 @@ $(document).on('click', '#restore', function (e) {
$(document).on('click', 'a.editor_filelink, a#editor_settingslink', function (e) { $(document).on('click', 'a.editor_filelink, a#editor_settingslink', function (e) {
e.preventDefault(); e.preventDefault();
var url = $(this).attr("href"); var url = $(this).attr("href");
load_file(url); var lineno = $(this).data("lineno");
load_file(url, lineno);
}); });
/* This method updates all editors already instantiated with the selected preferences*/ /* This method updates all editors already instantiated with the selected preferences*/
@@ -164,7 +170,7 @@ $(document).on('click', 'a.font_button', function (e) {
}); });
{{if len(request.args) > 1:}} {{if len(request.args) > 1:}}
load_file('{{=URL(f='edit', args=request.args, vars=request.get_vars)}}'); load_file('{{=URL(f='edit', args=request.args, vars=request.get_vars)}}', {{=request.vars.lineno or 1}});
{{pass}} {{pass}}
</script> </script>
@@ -227,7 +233,35 @@ $(document).on('click', 'a.font_button', function (e) {
<div id="myTabContent" class="tab-content"> <div id="myTabContent" class="tab-content">
</div> </div>
</div> </div>
<section id="windows_divs" class="tab-content ">
<div id="window_todo" class="tab-pane container-fluid">
{{=LOAD('default', 'todolist.load', vars={'app':app}, ajax=True, timeout=60000, times="infinity")}}
</div>
</section>
</div> </div>
{{block footer}}
<div id="" class="navbar navbar-inverse navbar-fixed-bottom">
<div class="navbar-inner">
<ul id="windows_hooks" class="nav">
<li class="">
<a href="#window_todo">TODO</a>
</li>
</ul>
</div>
</div>
<script>
$('#windows_hooks li a').click(function (e) {
e.preventDefault();
if ( $(this).parent('li').hasClass('active') ) {
$(this).parent('li').removeClass('active');
$($(this).attr('href')).removeClass('active');
} else {
$(this).tab('show');
}
});
</script>
{{end}}
<script> <script>
$(document).ready(function() { $(document).ready(function() {
var filesMenu = $('#files'); var filesMenu = $('#files');
+41 -28
View File
@@ -74,15 +74,15 @@
request.env['wsgi_url_scheme'], request.env['http_host'], request.env['wsgi_url_scheme'], request.env['http_host'],
URL(c='debug', f='toggle_breakpoint')))}}, sel); URL(c='debug', f='toggle_breakpoint')))}}, sel);
}); });
function makeMarker() { function makeMarker() {
var marker = document.createElement("div"); var marker = document.createElement("div");
marker.style.color = "#822"; marker.style.color = "#822";
marker.innerHTML = "●"; marker.innerHTML = "●";
marker.className = "breakpoint"; marker.className = "breakpoint";
return marker; return marker;
} }
{{if filetype in ('html', 'javascript', 'css'):}} {{if filetype in ('html', 'javascript', 'css'):}}
// must be here or break emmet/zencoding // must be here or break emmet/zencoding
CodeMirror.defaults.extraKeys["Ctrl-S"] = CodeMirror.defaults.extraKeys["Ctrl-S"] =
function(instance) { function(instance) {
@@ -94,26 +94,26 @@
CodeMirror.defaults.extraKeys["Shift-Esc"] = function(cm) { CodeMirror.defaults.extraKeys["Shift-Esc"] = function(cm) {
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false); if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
} }
{{pass}} {{pass}}
{{if filetype=='python':}} {{if filetype=='python':}}
// must be here or break emmet/zencoding for python // must be here or break emmet/zencoding for python
CodeMirror.defaults.extraKeys["Ctrl-S"] = CodeMirror.defaults.extraKeys["Ctrl-S"] =
function(instance) { function(instance) {
doClickSave();}; doClickSave();};
CodeMirror.defaults.extraKeys["Ctrl-Space"] = "autocomplete"; CodeMirror.defaults.extraKeys["Ctrl-Space"] = "autocomplete";
CodeMirror.defaults.extraKeys["Tab"] = "indentMore"; CodeMirror.defaults.extraKeys["Tab"] = "indentMore";
CodeMirror.defaults.extraKeys["Shift-Tab"] = "indentLess"; CodeMirror.defaults.extraKeys["Shift-Tab"] = "indentLess";
CodeMirror.defaults.extraKeys["Ctrl-F11"] = function(cm) { CodeMirror.defaults.extraKeys["Ctrl-F11"] = function(cm) {
cm.setOption("fullScreen", !cm.getOption("fullScreen")); cm.setOption("fullScreen", !cm.getOption("fullScreen"));
}, },
CodeMirror.defaults.extraKeys["Shift-Esc"] = function(cm) { CodeMirror.defaults.extraKeys["Shift-Esc"] = function(cm) {
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false); if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
} }
//for autocomplete //for autocomplete
CodeMirror.commands.autocomplete = function(cm) { CodeMirror.commands.autocomplete = function(cm) {
CodeMirror.showHint(cm, CodeMirror.pythonHint); CodeMirror.showHint(cm, CodeMirror.pythonHint);
} }
{{pass}} {{pass}}
store_changes_function = function(instance, changeObj) { store_changes_function = function(instance, changeObj) {
jQuery(instance).data('saved', false); jQuery(instance).data('saved', false);
instance.off("change", store_changes_function); instance.off("change", store_changes_function);
@@ -128,6 +128,19 @@
request.env['wsgi_url_scheme'], request.env['http_host'], request.env['wsgi_url_scheme'], request.env['http_host'],
URL(c='debug', f='list_breakpoints')))}}, editor); URL(c='debug', f='list_breakpoints')))}}, editor);
// TODO move it in a separated file
CodeMirror.defineExtension("centerOnCursor", function(limit) {
var coords = this.cursorCoords(null, "local");
if (this.getScrollerElement().clientHeight === 0 && limit !== 10) {
if (limit === undefined) limit = 1;
else limit += 1;
editor = this;
setTimeout(function() {editor.centerOnCursor()}, 100);
return;
}
clientHeight = (this.getScrollerElement().clientHeight / 2)
this.scrollTo(null, (coords.top + coords.bottom)/2 - 10);
});
</script> </script>
<div class="editor-bar-bottom" style="margin-top:9px;"> <div class="editor-bar-bottom" style="margin-top:9px;">
@@ -0,0 +1,15 @@
<div>
<!--div class="page-header"-->
<h4>TODO</h4>
<!--/div-->
<ul class="nav nav-list small-font">
{{for pos_file, file in enumerate(todo):}}
<li onclick="collapse('{{=pos_file}}_matches');"><a ><i class='icon-chevron-right'></i>{{=file['filename']}} <span class='small'>({{=len(file['matches'])}} TODO)</span></a></li>
<li id="{{=pos_file}}_matches" style="display: none;"><ul class="nav nav-list small-font">
{{for m in file['matches']:}}
<li><a href="{{=URL('edit', args=[app,file['dir'], file['filename']], vars={'lineno':m['lineno']}, extension="")}}" data-lineno="{{=m['lineno']-1}}" class="editor_filelink" >{{=m['text']}}</a></li>
{{pass}}
</ul></li>
{{pass}}
</ul>
</div>
+2
View File
@@ -55,6 +55,7 @@
<!-- FOOTER <!-- FOOTER
============== --> ============== -->
{{block footer}}
<footer id="footer" class="fixed"> <footer id="footer" class="fixed">
<p><span>{{=T('Powered by')}} {{=A('web2py', _href='http://www.web2py.com')}}&trade; {{=T('created by')}} Massimo Di Pierro &copy;2007-{{=request.now.year}} <p><span>{{=T('Powered by')}} {{=A('web2py', _href='http://www.web2py.com')}}&trade; {{=T('created by')}} Massimo Di Pierro &copy;2007-{{=request.now.year}}
{{if hasattr(T,'get_possible_languages_info'):}} {{if hasattr(T,'get_possible_languages_info'):}}
@@ -68,6 +69,7 @@
</span>{{pass}} </span>{{pass}}
</p> </p>
</footer><!-- /#footer --> </footer><!-- /#footer -->
{{end}}
<!-- BS JAVASCRIPT <!-- BS JAVASCRIPT
====================== --> ====================== -->