Bugfix and documentation.
- Fixed a bug in ts_api:list_timelines/2 and ts_api:list_entries/3, which respond only to GET requests but were looking for POST data. - Added documentation for ts.js.
This commit is contained in:
parent
efab46f167
commit
348c73a36f
Binary file not shown.
Binary file not shown.
@ -240,16 +240,16 @@ get_user(YArg, Username) ->
|
|||||||
|
|
||||||
list_timelines(YArg, Username) ->
|
list_timelines(YArg, Username) ->
|
||||||
% pull out the POST data
|
% pull out the POST data
|
||||||
PostData = yaws_api:parse_post(YArg),
|
QueryData = yaws_api:parse_query(YArg),
|
||||||
|
|
||||||
% read or default the Start
|
% read or default the Start
|
||||||
Start = case lists:keyfind(start, 1, PostData) of
|
Start = case lists:keyfind(start, 1, QueryData) of
|
||||||
{start, StartVal} -> list_to_integer(StartVal);
|
{start, StartVal} -> list_to_integer(StartVal);
|
||||||
false -> 0
|
false -> 0
|
||||||
end,
|
end,
|
||||||
|
|
||||||
% read or default the Length
|
% read or default the Length
|
||||||
Length = case lists:keyfind(length, 1, PostData) of
|
Length = case lists:keyfind(length, 1, QueryData) of
|
||||||
{length, LengthVal} ->
|
{length, LengthVal} ->
|
||||||
erlang:min(list_to_integer(LengthVal), 50);
|
erlang:min(list_to_integer(LengthVal), 50);
|
||||||
false -> 50
|
false -> 50
|
||||||
@ -340,35 +340,35 @@ delete_timeline(_YArg, _Username, _TimelineId) -> {status, 405}.
|
|||||||
|
|
||||||
list_entries(YArg, Username, TimelineId) ->
|
list_entries(YArg, Username, TimelineId) ->
|
||||||
% pull out the POST data
|
% pull out the POST data
|
||||||
PostData = yaws_api:parse_post(YArg),
|
QueryData = yaws_api:parse_query(YArg),
|
||||||
|
|
||||||
% first determine if we are listing by date
|
% first determine if we are listing by date
|
||||||
case {ts_timeline:lookup(Username, TimelineId),
|
case {ts_timeline:lookup(Username, TimelineId),
|
||||||
lists:keyfind(byDate, 1, PostData)} of
|
lists:keyfind("byDate", 1, QueryData)} of
|
||||||
|
|
||||||
{no_record, _ByDateField} -> make_json_404(
|
{no_record, _ByDateField} -> make_json_404(
|
||||||
[{status, "no such timeline"},
|
[{status, "no such timeline"},
|
||||||
{see_docs, "/ts_api_doc/entries.html#LIST"}]);
|
{see_docs, "/ts_api_doc/entries.html#LIST"}]);
|
||||||
|
|
||||||
% listing by date range
|
% listing by date range
|
||||||
{Timeline, {byDate, "true"}} ->
|
{Timeline, {"byDate", "true"}} ->
|
||||||
|
|
||||||
% look for the start date; default to the beginning of the timeline
|
% look for the start date; default to the beginning of the timeline
|
||||||
StartDate = case lists:keyfind(startDate, 1, PostData) of
|
StartDate = case lists:keyfind("startDate", 1, QueryData) of
|
||||||
% TODO: error handling if the date is badly formatted
|
% TODO: error handling if the date is badly formatted
|
||||||
{startDate, StartDateVal} -> ts_json:decode_date(StartDateVal);
|
{startDate, StartDateVal} -> ts_json:decode_date(StartDateVal);
|
||||||
false -> Timeline#ts_timeline.created
|
false -> Timeline#ts_timeline.created
|
||||||
end,
|
end,
|
||||||
|
|
||||||
% look for end date; default to right now
|
% look for end date; default to right now
|
||||||
EndDate = case lists:keyfind(endDate, 1, PostData) of
|
EndDate = case lists:keyfind("endDate", 1, QueryData) of
|
||||||
% TODO: error handling if the date is badly formatted
|
% TODO: error handling if the date is badly formatted
|
||||||
{endDate, EndDateVal} -> ts_json:decode_date(EndDateVal);
|
{endDate, EndDateVal} -> ts_json:decode_date(EndDateVal);
|
||||||
false -> calendar:now_to_universal_time(erlang:now())
|
false -> calendar:now_to_universal_time(erlang:now())
|
||||||
end,
|
end,
|
||||||
|
|
||||||
% read sort order and list entries
|
% read sort order and list entries
|
||||||
Entries = case lists:keyfind(order, 1, PostData) of
|
Entries = case lists:keyfind("order", 1, QueryData) of
|
||||||
% descending sort order
|
% descending sort order
|
||||||
{order, "desc"} -> ts_entry:list_desc(
|
{order, "desc"} -> ts_entry:list_desc(
|
||||||
{Username, TimelineId}, StartDate, EndDate);
|
{Username, TimelineId}, StartDate, EndDate);
|
||||||
@ -391,24 +391,24 @@ list_entries(YArg, Username, TimelineId) ->
|
|||||||
_Other ->
|
_Other ->
|
||||||
|
|
||||||
% read or default the Start
|
% read or default the Start
|
||||||
Start = case lists:keyfind(start, 1, PostData) of
|
Start = case lists:keyfind("start", 1, QueryData) of
|
||||||
{start, StartVal} -> list_to_integer(StartVal);
|
{start, StartVal} -> list_to_integer(StartVal);
|
||||||
false -> 0
|
false -> 0
|
||||||
end,
|
end,
|
||||||
|
|
||||||
% read or default the Length
|
% read or default the Length
|
||||||
Length = case lists:keyfind(length, 1, PostData) of
|
Length = case lists:keyfind("length", 1, QueryData) of
|
||||||
{length, LengthVal} ->
|
{length, LengthVal} ->
|
||||||
erlang:min(list_to_integer(LengthVal), 500);
|
erlang:min(list_to_integer(LengthVal), 500);
|
||||||
false -> 50
|
false -> 50
|
||||||
end,
|
end,
|
||||||
|
|
||||||
% read sort order and list entries
|
% read sort order and list entries
|
||||||
Entries = case lists:keyfind(order, 1, PostData) of
|
Entries = case lists:keyfind("order", 1, QueryData) of
|
||||||
{order, "desc"} -> ts_entry:list_desc(
|
{"order", "desc"} -> ts_entry:list_desc(
|
||||||
{Username, TimelineId}, Start, Length);
|
{Username, TimelineId}, Start, Length);
|
||||||
|
|
||||||
_Other -> ts_entry:list_asc(
|
_UnknownOrder -> ts_entry:list_asc(
|
||||||
{Username, TimelineId}, Start, Length)
|
{Username, TimelineId}, Start, Length)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
76
www/js/ts.js
76
www/js/ts.js
@ -1,121 +1,179 @@
|
|||||||
|
var user = [];
|
||||||
|
var timelines = [];
|
||||||
|
var activeTimeline = [];
|
||||||
|
var entries = [];
|
||||||
|
|
||||||
|
var lastEntryBar;
|
||||||
|
|
||||||
|
/* Setup after the document is ready for manipulation. */
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
|
||||||
|
lastEntryBar = $("#last-entry");
|
||||||
|
|
||||||
|
// wire the login dialog using jQuery UI
|
||||||
$("#login-dialog").dialog({
|
$("#login-dialog").dialog({
|
||||||
autoOpen: false,
|
autoOpen: false,
|
||||||
height: 300,
|
height: 300,
|
||||||
width: 300,
|
width: 300,
|
||||||
modal: true,
|
modal: true,
|
||||||
buttons: {
|
buttons: { Login: function(){login()} }
|
||||||
Login: function(){login()}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// try to load user information for an authenticated user
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/ts_api/users/",
|
url: "/ts_api/users/",
|
||||||
type: "GET",
|
type: "GET",
|
||||||
|
|
||||||
error: function(jqXHR, textStatus, error) {
|
error: function(jqXHR, textStatus, error) {
|
||||||
|
// assume there is no authenticated user, show login dialog
|
||||||
$("#login-dialog").dialog("open"); },
|
$("#login-dialog").dialog("open"); },
|
||||||
|
|
||||||
success: function(data, textStatus, jqXHR) {
|
success: function(data, textStatus, jqXHR) {
|
||||||
|
// load the user information
|
||||||
loadUser(data.user.username); }});
|
loadUser(data.user.username); }});
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
var user = []
|
/* Read the user's credentials from the login form and perform
|
||||||
var timelines = []
|
* an AJAX request to the API to set the session cookie. */
|
||||||
var activeTimeline = []
|
|
||||||
|
|
||||||
function login() {
|
function login() {
|
||||||
|
// lookup the login dialog elements
|
||||||
var name = $("#login-name");
|
var name = $("#login-name");
|
||||||
var pwd = $("#login-password");
|
var pwd = $("#login-password");
|
||||||
|
|
||||||
|
// call the API via AJAX
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/ts_api/login",
|
url: "/ts_api/login",
|
||||||
processData: false,
|
processData: false,
|
||||||
data: JSON.stringify({username: name.val(), password: pwd.val()}),
|
data: JSON.stringify({username: name.val(), password: pwd.val()}),
|
||||||
type: "POST",
|
type: "POST",
|
||||||
|
|
||||||
error: function(jqXHR, textStatus, error) {
|
error: function(jqXHR, textStatus, error) {
|
||||||
|
// assuming bad credentials (possible server error or bad request,
|
||||||
|
// we should check that, FIXME
|
||||||
var tips = $(".validate-tips");
|
var tips = $(".validate-tips");
|
||||||
tips.text("Incorrect username/password combination.");
|
tips.text("Incorrect username/password combination.");
|
||||||
tips.addClass("ui-state-error");
|
tips.addClass("ui-state-error");
|
||||||
tips.slideDown();
|
tips.slideDown();
|
||||||
},
|
},
|
||||||
|
|
||||||
success: function(data, textStatus, jqXHR) {
|
success: function(data, textStatus, jqXHR) {
|
||||||
|
// load the user information and hide the login dialog
|
||||||
loadUser(name.val());
|
loadUser(name.val());
|
||||||
$("#login-dialog").dialog("close");
|
$("#login-dialog").dialog("close");
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* End the current user session and expire any session credentials we
|
||||||
|
* have aquired. */
|
||||||
function logout(event) {
|
function logout(event) {
|
||||||
alert("TODO: log user out via AJAX.");
|
alert("TODO: log user out via AJAX.");
|
||||||
|
// TODO: wipe username, timeline, entry variables and displays
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Load and display the user's information and timelines. */
|
||||||
function loadUser(username) {
|
function loadUser(username) {
|
||||||
|
|
||||||
|
// call the user_summary API function
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/ts_api/app/user_summary/" + username,
|
url: "/ts_api/app/user_summary/" + username,
|
||||||
type: "GET",
|
type: "GET",
|
||||||
|
|
||||||
success: function(data, textStatus, jqXHR) {
|
success: function(data, textStatus, jqXHR) {
|
||||||
|
// set the user variable
|
||||||
user = data.user;
|
user = data.user;
|
||||||
|
|
||||||
|
// set the timelines variable
|
||||||
timelines = data.timelines;
|
timelines = data.timelines;
|
||||||
|
|
||||||
|
// update the user id display
|
||||||
$("#fullname").text(user.name);
|
$("#fullname").text(user.name);
|
||||||
$("#username").text("- " + user.username);
|
$("#username").text("- " + user.username);
|
||||||
|
|
||||||
|
// pre-populate the editable user-info fields
|
||||||
// TODO: not working
|
// TODO: not working
|
||||||
$("#fullname-input").text(user.name);
|
$("#fullname-input").text(user.name);
|
||||||
$("#email-input").text(user.email);
|
$("#email-input").text(user.email);
|
||||||
|
|
||||||
|
// set the active timeline to the first in the list
|
||||||
|
// TODO: implement a mechanism to remember the last used timeline
|
||||||
|
// on the server side and respond to that here.
|
||||||
activeTimeline = timelines[0];
|
activeTimeline = timelines[0];
|
||||||
|
|
||||||
|
// update the timeline display
|
||||||
$("#timeline-name").text(activeTimeline.timeline_id + " |");
|
$("#timeline-name").text(activeTimeline.timeline_id + " |");
|
||||||
$("#timeline-desc").text(activeTimeline.description);
|
$("#timeline-desc").text(activeTimeline.description);
|
||||||
|
|
||||||
loadTimeline(user, activeTimeline)
|
// TODO: populate the drop-down list for the available timeline
|
||||||
|
// choices
|
||||||
|
|
||||||
|
// load the entries for this timeline
|
||||||
|
loadEntries(user, activeTimeline)
|
||||||
},
|
},
|
||||||
|
|
||||||
error: function(jqXHR, textStatus, error) {
|
error: function(jqXHR, textStatus, error) {
|
||||||
|
// TODO
|
||||||
alert("TODO: handle error for user load.")
|
alert("TODO: handle error for user load.")
|
||||||
|
alert(jqXHR.responseText)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadTimeline(user, timeline) {
|
/* Read the first 50 entries for a timeline. */
|
||||||
|
function loadEntries(user, timeline) {
|
||||||
|
|
||||||
|
// call the API list_entries function via AJAX
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/ts_api/entries/" + user.username + "/" + timeline.timeline_id,
|
url: "/ts_api/entries/" + user.username + "/" + timeline.timeline_id,
|
||||||
type: "GET",
|
type: "GET",
|
||||||
|
|
||||||
success: function(data, textStatus, jqXHR) {
|
success: function(data, textStatus, jqXHR) {
|
||||||
|
entries = data.entries;
|
||||||
|
|
||||||
|
// push the entries onto the page
|
||||||
|
displayEntries(entries)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Show/hide the editable user-info panel. */
|
||||||
function toggleUserInfo(event) {
|
function toggleUserInfo(event) {
|
||||||
$("#user-info").slideToggle("slow");
|
$("#user-info").slideToggle("slow");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Show/hide the password change controls. */
|
||||||
function showChangePwd(event) { $("#change-pwd").slideToggle("slow"); }
|
function showChangePwd(event) { $("#change-pwd").slideToggle("slow"); }
|
||||||
|
|
||||||
|
/* Update the user information based on the editable user-info panel. */
|
||||||
function updateUser(event) {
|
function updateUser(event) {
|
||||||
alert("TODO: update user via AJAX.");
|
alert("TODO: update user via AJAX.");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Show/hide the editable timeline-info panel. */
|
||||||
function toggleTimelineInfo(event) {
|
function toggleTimelineInfo(event) {
|
||||||
$("#timeline-info").slideToggle("slow");
|
$("#timeline-info").slideToggle("slow");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Show the change timeline menu. */
|
||||||
function showTimelineMenu(event) {
|
function showTimelineMenu(event) {
|
||||||
alert("TODO: show other timelines via a popup menu");
|
alert("TODO: show other timelines via a popup menu");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update the timeline details based on the editable timeline-info panel. */
|
||||||
function updateTimeline(event) {
|
function updateTimeline(event) {
|
||||||
alert("TODO: update timeline via AJAX.");
|
alert("TODO: update timeline via AJAX.");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Show/hide the add notes panel. */
|
||||||
function showNewNotes(event) { $("#add-notes").slideToggle("slow"); }
|
function showNewNotes(event) { $("#add-notes").slideToggle("slow"); }
|
||||||
|
|
||||||
|
/* Create a new entry based on the user's input in the new-entry panel. */
|
||||||
function newEntry(event) {
|
function newEntry(event) {
|
||||||
alert("TODO: create entry vi AJAX");
|
alert("TODO: create entry vi AJAX");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user