* stupidtable.js:

* jquery.tablesorter.js:
* jquery.tablesorter.min.js: now using "stupid table", that fixes the
  row additions and deletions

* ChooseADate.aspx: Page to choose a date between valid candidates

* YavscModel.csproj:
* FreeDate.cs: a free date

* FrontOfficeApiController.cs: cleanning using clauses

* FrontOfficeController.cs: - cleanning using clauses
- check user role before editing the model object

* GoogleController.cs: view "ChooseADate" in successfull response to
  "DateQuery"


* WorkFlowController.cs: check user's role to drop the estimate.

* Estimate.aspx: * now using "stupid table", that fixes the row
  additions and deletions.
* the user interface more intuitive at row deletion 

* DateQuery.aspx: changed the query parameters

* Web.csproj: google date + stupidtable - tablesorter

* IContentProvider.cs: cleaned an unused "using" clause

* WorkFlowManager.cs: cleanning spaces
vnext
Paul Schneider 10 years ago
parent a81701008d
commit e99c03e54a
15 changed files with 286 additions and 1092 deletions

@ -10,7 +10,6 @@ using System.Web;
using System.Linq;
using System.IO;
using System.Net;
using Yavsc;
using System.Web.Security;
using Yavsc.Model.WorkFlow;
using System.Reflection;

@ -8,7 +8,6 @@ using Yavsc.Controllers;
using System.Collections.Generic;
using Yavsc.Model;
using Yavsc.Model.WorkFlow;
using Yavsc;
using System.Web.Security;
using System.Threading;
using Yavsc.Model.FrontOffice;
@ -65,7 +64,7 @@ namespace Yavsc.Controllers
public ActionResult Estimate (Estimate model, string submit)
{
// Obsolete, set in master page
ViewData ["WebApiBase"] = "http://" + Request.Url.Authority + "/api";
ViewData ["WebApiBase"] = Url.Content(Yavsc.WebApiConfig.UrlPrefixRelative);
ViewData ["WABASEWF"] = ViewData ["WebApiBase"] + "/WorkFlow";
if (submit == null) {
if (model.Id > 0) {
@ -83,16 +82,17 @@ namespace Yavsc.Controllers
throw new UnauthorizedAccessException ("You're not allowed to view this estimate");
}
} else {
string username = HttpContext.User.Identity.Name;
string username = Membership.GetUser().UserName;
if (username != model.Responsible
&& !Roles.IsUserInRole ("FrontOffice"))
throw new UnauthorizedAccessException ("You're not allowed to modify this estimate");
if (model.Id == 0) {
model.Responsible = username;
ModelState.Clear ();
ModelState.Clear ();
// TODO better, or ensure that the model state is checked
// before insertion
}
if (ModelState.IsValid) {
if (username != model.Responsible
&& !Roles.IsUserInRole ("FrontOffice"))
throw new UnauthorizedAccessException ("You're not allowed to modify this estimate");
if (model.Id == 0)
model = wfmgr.CreateEstimate (
username,

@ -334,7 +334,7 @@ namespace Yavsc.Controllers
return View ("GoogleErrorMessage", ex);
}
return View (res);
return View ("ChooseADate",res);
}
return View (model);
}

@ -70,6 +70,14 @@ namespace Yavsc.ApiControllers
[Authorize]
public void DropEstimate(long estid)
{
string username = Membership.GetUser().UserName;
Estimate e = wfmgr.GetEstimate (estid);
if (e == null)
throw new InvalidOperationException("not an estimate id:"+estid);
if (username != e.Responsible
&& !Roles.IsUserInRole ("FrontOffice"))
throw new UnauthorizedAccessException ("You're not allowed to drop this estimate");
wfmgr.DropEstimate (estid);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -0,0 +1,135 @@
// Stupid jQuery table plugin.
(function($) {
$.fn.stupidtable = function(sortFns) {
return this.each(function() {
var $table = $(this);
sortFns = sortFns || {};
sortFns = $.extend({}, $.fn.stupidtable.default_sort_fns, sortFns);
$table.data('sortFns', sortFns);
$table.on("click.stupidtable", "thead th", function() {
$(this).stupidsort();
});
});
};
// Expects $("#mytable").stupidtable() to have already been called.
// Call on a table header.
$.fn.stupidsort = function(force_direction){
var $this_th = $(this);
var th_index = 0; // we'll increment this soon
var dir = $.fn.stupidtable.dir;
var $table = $this_th.closest("table");
var datatype = $this_th.data("sort") || null;
// No datatype? Nothing to do.
if (datatype === null) {
return;
}
// Account for colspans
$this_th.parents("tr").find("th").slice(0, $(this).index()).each(function() {
var cols = $(this).attr("colspan") || 1;
th_index += parseInt(cols,10);
});
var sort_dir;
if(arguments.length == 1){
sort_dir = force_direction;
}
else{
sort_dir = force_direction || $this_th.data("sort-default") || dir.ASC;
if ($this_th.data("sort-dir"))
sort_dir = $this_th.data("sort-dir") === dir.ASC ? dir.DESC : dir.ASC;
}
$table.trigger("beforetablesort", {column: th_index, direction: sort_dir});
// More reliable method of forcing a redraw
$table.css("display");
// Run sorting asynchronously on a timout to force browser redraw after
// `beforetablesort` callback. Also avoids locking up the browser too much.
setTimeout(function() {
// Gather the elements for this column
var column = [];
var sortFns = $table.data('sortFns');
var sortMethod = sortFns[datatype];
var trs = $table.children("tbody").children("tr");
// Extract the data for the column that needs to be sorted and pair it up
// with the TR itself into a tuple. This way sorting the values will
// incidentally sort the trs.
trs.each(function(index,tr) {
var $e = $(tr).children().eq(th_index);
var sort_val = $e.data("sort-value");
// Store and read from the .data cache for display text only sorts
// instead of looking through the DOM every time
if(typeof(sort_val) === "undefined"){
var txt = $e.text();
$e.data('sort-value', txt);
sort_val = txt;
}
column.push([sort_val, tr]);
});
// Sort by the data-order-by value
column.sort(function(a, b) { return sortMethod(a[0], b[0]); });
if (sort_dir != dir.ASC)
column.reverse();
// Replace the content of tbody with the sorted rows. Strangely
// enough, .append accomplishes this for us.
trs = $.map(column, function(kv) { return kv[1]; });
$table.children("tbody").append(trs);
// Reset siblings
$table.find("th").data("sort-dir", null).removeClass("sorting-desc sorting-asc");
$this_th.data("sort-dir", sort_dir).addClass("sorting-"+sort_dir);
$table.trigger("aftertablesort", {column: th_index, direction: sort_dir});
$table.css("display");
}, 10);
return $this_th;
};
// Call on a sortable td to update its value in the sort. This should be the
// only mechanism used to update a cell's sort value. If your display value is
// different from your sort value, use jQuery's .text() or .html() to update
// the td contents, Assumes stupidtable has already been called for the table.
$.fn.updateSortVal = function(new_sort_val){
var $this_td = $(this);
if($this_td.is('[data-sort-value]')){
// For visual consistency with the .data cache
$this_td.attr('data-sort-value', new_sort_val);
}
$this_td.data("sort-value", new_sort_val);
return $this_td;
};
// ------------------------------------------------------------------
// Default settings
// ------------------------------------------------------------------
$.fn.stupidtable.dir = {ASC: "asc", DESC: "desc"};
$.fn.stupidtable.default_sort_fns = {
"int": function(a, b) {
return parseInt(a, 10) - parseInt(b, 10);
},
"float": function(a, b) {
return parseFloat(a) - parseFloat(b);
},
"string": function(a, b) {
return a.localeCompare(b);
},
"string-ins": function(a, b) {
a = a.toLocaleLowerCase();
b = b.toLocaleLowerCase();
return a.localeCompare(b);
}
};
})(jQuery);

@ -1,7 +1,12 @@
<%@ Page Title="Devis" Language="C#" Inherits="System.Web.Mvc.ViewPage<Estimate>" MasterPageFile="~/Models/App.master" %>
<asp:Content ContentPlaceHolderID="head" ID="head1" runat="server" >
<script type="text/javascript" src="<%=Url.Content("~/Scripts/jquery.tablesorter.js")%>"></script>
<script type="text/javascript" src="<%=Url.Content("~/Scripts/stupidtable.js")%>"></script>
<script>
$(function(){
$("#tbwrts").stupidtable();
});
</script>
<script type="text/javascript" src="<%=Url.Content("~/Scripts/jquery.validate.js")%>"></script>
<link rel="stylesheet" href="<%=Url.Content("~/Theme/dark/style.css")%>" type="text/css" media="print, projection, screen" />
</asp:Content>
@ -29,13 +34,14 @@
<% if (Model.Id>0) { %>
<table class="tablesorter">
<table id="tbwrts">
<thead>
<tr>
<th><%=Yavsc.Model.LocalizedText.Description%></th>
<th><%=Yavsc.Model.LocalizedText.Product_reference%></th>
<th><%=Yavsc.Model.LocalizedText.Count%></th>
<th><%=Yavsc.Model.LocalizedText.Unitary_cost%></th>
<th data-sort="string"><%=Yavsc.Model.LocalizedText.Description%></th>
<th data-sort="string"><%=Yavsc.Model.LocalizedText.Product_reference%></th>
<th data-sort="int"><%=Yavsc.Model.LocalizedText.Count%></th>
<th data-sort="float"><%=Yavsc.Model.LocalizedText.Unitary_cost%></th>
</tr>
</thead>
<tbody id="wrts">
@ -47,6 +53,9 @@
<td><%=wr.ProductReference%></td>
<td><%=wr.Count%></td>
<td><%=wr.UnitaryCost%></td>
<td>
<input type="button" value="X" class="actionlink rowbtnrm"/>
</td>
</tr>
<% } %>
</tbody>
@ -54,15 +63,12 @@
<% } %>
<% } %>
<aside>
<a class="actionlink" href="<%=ViewData["WebApiBase"]%>/FrontOffice/EstimateToTex?estimid=<%=Model.Id%>"><%= LocalizedText.Tex_version %></a>
<a class="actionlink" href="<%=ViewData["WebApiBase"]%>/FrontOffice/EstimateToPdf?estimid=<%=Model.Id%>"><%= LocalizedText.Pdf_version %></a>
</aside>
</asp:Content>
<asp:Content ContentPlaceHolderID="MASContent" ID="MASContent1" runat="server">
<% ViewData["EstimateId"]=Model.Id; %>
<aside>
<% ViewData["EstimateId"]=Model.Id; %>
<%= Html.Partial("Writting",new Writting(),new ViewDataDictionary(ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
@ -77,11 +83,11 @@
<input type="button" id="btnnew" value="Nouvelle écriture" class="actionlink"/>
<input type="button" id="btncreate" value="Ecrire" class="actionlink"/>
<input type="button" id="btnmodify" value="Modifier" class="hidden actionlink"/>
<input type="button" id="btndrop" value="Supprimer" class="hidden actionlink"/>
</div> </form>
<tt id="msg" class="hidden message"></tt>
<style>
th { cursor:pointer; }
.row { cursor:pointer; }
table.tablesorter td:hover { background-color: rgba(0,64,0,0.5); }
.hidden { display:none; }
@ -126,21 +132,23 @@
}
}
function delRow() {
function delRow(e) {
e.stopPropagation(); // do not edit this row on this click
// from <row id= ...><tr><td><input type="button" >
var hid=e.delegateTarget.parentNode.parentNode.id;
var vwrid = Number(hid.substr(2));
$.ajax({
url: "<%=Url.Content("~/api/WorkFlow/DropWritting")%>",
type: "Get",
data: { wrid: wr_Id.value },
data: { wrid: vwrid },
contentType: 'application/json; charset=utf-8',
success: function () {
$("#wr"+wr_Id.value).remove();
$("#wr_Id").val(0);
$("#wr_UnitaryCost").val(0);
$("#wr_Count").val(0);
$("#wr_Description").val();
$("#wr_ProductReference").val();
wredit(0);
message(false);
$("#wr"+vwrid).remove(); // removes clicked row
wredit(0); // set current form target id to none
message(false); // hides current message
// $("#tbwrts").update();// rebuilds the cache for the tablesorter js
// $("#tbwrts").tablesorter( {sortList: [[0,0], [1,0]]} ); // .update();
},
error: function (xhr, ajaxOptions, thrownError) {
message(xhr.status+" : "+xhr.responseText);}
@ -169,7 +177,7 @@
function addRow(){
var wrt = GetWritting();
var wrt = GetWritting(); // gets a writting object from input controls
var estid = parseInt($("#Id").val());
$("#Err_wr_Description").text("");
@ -193,8 +201,12 @@ function addRow(){
$("<td>"+wrt.ProductReference+"</td>").appendTo("#"+wridval);
$("<td>"+wrt.Count+"</td>").appendTo("#"+wridval);
$("<td>"+wrt.UnitaryCost+"</td>").appendTo("#"+wridval);
var btrm = $("<input type=\"button\" value=\"X\" class=\"actionlink rowbtnrm\"/>");
$("<td></td>").append(btrm).appendTo("#"+wridval);
btrm.click(function (e) {delRow(e);});
$("#"+wridval).click(function(ev){onEditRow(ev);});
$(".tablesorter").tablesorter( {sortList: [[0,0], [1,0]]} );
// $("#tbwrts").tablesorter( {sortList: [[0,0], [1,0]]} ); // .update();
message(false);
},
@ -225,15 +237,11 @@ function addRow(){
}
$(document).ready(function () {
// bug when no row:
<% if (Model.Lines != null) if (Model.Lines.Length>0) { %>
$(".tablesorter").tablesorter( {sortList: [[0,0], [1,0]]} );
<% } %>
$("#btncreate").click(addRow);
$("#btnmodify").click(setRow);
$("#btndrop").click(delRow);
$(".row").click(function (e) {onEditRow(e);});
$(".rowbtnrm").click(function (e) {delRow(e);});
$("#btnnew").click(function () {
wredit(0);
$("#wr_Description").val("");
@ -243,7 +251,12 @@ function addRow(){
});
});
</script>
<a class="actionlink" href="<%=Url.Content(Yavsc.WebApiConfig.UrlPrefixRelative)%>/FrontOffice/EstimateToTex?estimid=<%=Model.Id%>"><%= LocalizedText.Tex_version %></a>
<a class="actionlink" href="<%=Url.Content(Yavsc.WebApiConfig.UrlPrefixRelative)%>/FrontOffice/EstimateToPdf?estimid=<%=Model.Id%>"><%= LocalizedText.Pdf_version %></a>
</aside>
</asp:Content>

@ -0,0 +1,15 @@
<%@ Page Title="Google calendar usage" Language="C#" Inherits="System.Web.Mvc.ViewPage<FreeDateSet>" MasterPageFile="~/Models/App.master" %>
<asp:Content ContentPlaceHolderID="MainContent" ID="MainContentContent" runat="server">
<% using ( Html.BeginForm("SetCalendar","Google") ) { %>
<% foreach (CalendarListEntry e in Model.items.Where(x=>x.accessRole=="owner")) { %>
<input type="radio" name="calchoice" id="calchoice" value="<%=e.id%>" >
<%=Html.Encode(e.summary)%> <br>
<i><%=Html.Encode(e.description)%></i> <br>
<% } %>
<input type="hidden" name="returnUrl" id="returnUrl" value="<%=Html.Encode(ViewData["returnUrl"])%>">
<input type="submit">
<% } %>
</asp:Content>

@ -46,27 +46,32 @@ return Globalize.format( new Date(value), "t" );
</script>
</asp:Content>
<asp:Content ContentPlaceHolderID="MainContent" ID="MainContentContent" runat="server">
<asp:Content ContentPlaceHolderID="MainContent" ID="MainContentContent" runat="server">
<% using ( Html.BeginForm("DateQuery","Google") ) { %>
<p>Période de recherche:</p>
<p>
<%= Html.LabelFor(model=>model.PreferedDate) %>:<br>
Le <%= Html.TextBoxFor(model=>model.PreferedDate) %>
<%= Html.ValidationMessageFor(model=>model.PreferedDate) %>
</p>
<p>
<%= Html.LabelFor(model=>model.PreferedHour) %>:<br>
Le <%= Html.TextBoxFor(model=>model.PreferedHour) %>
<%= Html.ValidationMessageFor(model=>model.PreferedHour) %>
</p>
<p>
<%= Html.LabelFor(model=>model.MaxDate) %>:<br>
Le <%= Html.TextBoxFor(model=>model.MaxDate) %>
<%= Html.ValidationMessageFor(model=>model.MaxDate) %>
à
<%= Html.TextBoxFor(model=>model.MaxTime) %>
<%= Html.ValidationMessageFor(model=>model.MaxTime) %>
</p>
<p>
Durée minimale : <%= Html.TextBoxFor(model=>model.Duration) %>
<%= Html.ValidationMessageFor(model=>model.Duration) %>
<%= Html.LabelFor(model=>model.MinDuration) %>:<br>
<%= Html.TextBoxFor(model=>model.MinDuration) %>
<%= Html.ValidationMessageFor(model=>model.MinDuration) %>
</p>
<p>
<%= Html.LabelFor(model=>model.UserName) %>:<br>
<%= Html.TextBoxFor(model=>model.UserName) %><br>

@ -268,8 +268,6 @@
<Content Include="Views\Google\Login.aspx" />
<Content Include="Views\Google\Auth.aspx" />
<Content Include="Scripts\jquery.metadata.js" />
<Content Include="Scripts\jquery.tablesorter.js" />
<Content Include="Scripts\jquery.tablesorter.min.js" />
<Content Include="Scripts\rangyinputs-jquery-1.1.2.js" />
<Content Include="images\sign-in-with-google.png" />
<Content Include="Views\Account\Unregister.aspx" />
@ -675,6 +673,8 @@
<Content Include="Views\Account\MyProfile.aspx" />
<Content Include="Views\FrontOffice\Basket.aspx" />
<Content Include="Theme\dark\rect.png" />
<Content Include="Views\Google\ChooseADate.aspx" />
<Content Include="Scripts\stupidtable.js" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />

@ -0,0 +1,53 @@
//
// FreeDate.cs
//
// Author:
// Paul Schneider <paulschneider@free.fr>
//
// Copyright (c) 2015 Paul Schneider
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace Yavsc.Model.Google
{
/// <summary>
/// Free date.
/// </summary>
public class FreeDateSet
{
/// <summary>
/// Gets or sets the reference.
/// </summary>
/// <value>The reference.</value>
public DateTime [] Values { get; set; }
/// <summary>
/// Gets or sets the duration.
/// </summary>
/// <value>The duration.</value>
public TimeSpan Duration { get; set; }
/// <summary>
/// Gets or sets the attendees.
/// </summary>
/// <value>The attendees.</value>
public string Attendees { get; set; }
/// <summary>
/// Gets or sets the location.
/// </summary>
/// <value>The location.</value>
public string Location { get; set; }
}
}

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Yavsc.Model.FrontOffice;
namespace Yavsc.Model.WorkFlow

@ -92,6 +92,9 @@ namespace Yavsc.Model.WorkFlow
{
ContentProvider.DropEstimate(estid);
}
IContentProvider contentProvider;
/// <summary>
/// Gets the content provider.
@ -131,8 +134,6 @@ namespace Yavsc.Model.WorkFlow
contentProvider.Initialize (confprov.Name, config);
return contentProvider;
}
}

@ -139,6 +139,7 @@
<Compile Include="FrontOffice\Catalog\Billing\Price.cs" />
<Compile Include="OtherWebException.cs" />
<Compile Include="WorkFlow\Automate.cs" />
<Compile Include="Google\FreeDate.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

Loading…