Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

2532 lines (2209 sloc) 87.522 kB
% -*- Erlang -*-
% File: wiki.erl (~jb/work/wiki/src/wiki.erl)
% Author: Joe Armstrong
% Author: Johan Bevemyr
% Purpose: Wiki web in Erlang
%%
%% History: Ported and partly rewritten by Johan Bevemyr for
%% the yaws http server.
%%
%% History: First started by Luke Gorrie who wrote the first
%% Erlang wiki. Subsequently re-written many times by Joe Armstrong
%% and Luke Gorrie.
%% This version by Joe Armstrong.
%% Thanks to Luke and Robert Virding for many helpfull
%% discussions clarifying the design.
%% This also makes use of the new pico_http_server which has
%% a much simplified interface.
-module('wiki').
-author('jb@son.bevemyr.com').
-compile(export_all).
-export([showPage/3, createNewPage/3, showHistory/3, allPages/3,
lastEdited/3, wikiZombies/3, editPage/3, editFiles/3,
previewNewPage/3, allRefsToMe/3, deletePage/3,
editTag/3, finalDeletePage/3, storePage/3, putPassword/3,
storeNewPage/3, previewPage/3, previewTagged/3, copyFiles/3,
deleteFiles/3, addFile/1, addFile/2,
addFile/3, storeTagged/3, fixupFiles/3,
sendMeThePassword/3, storeFiles/3, showOldPage/3,
changePassword/3, changePassword2/3, getThumb/3,
getMidSize/3, thumbIndex/3, searchPages/3]).
-export([slideShow/3]).
-export([show/2, ls/1, h1/1, read_page/2, p/1,
str2urlencoded/1, session_manager_init/2]).
-export([getPassword/1]).
-export([importFiles/1]).
-import(lists, [reverse/1, map/2, sort/1]).
-import(wiki_templates, [template/5, template2/5]).
-include("../../../include/yaws_api.hrl").
-include_lib("kernel/include/file.hrl").
% This should be -include:ed instead
showPage(Params, Root, Prefix) ->
Page = getnode(Params),
if
Page == undefined ->
html_error(invalid_request);
true ->
{WobFile, FileDir} = page2filename(Page, Root),
case file:read_file(WobFile) of
{ok, Bin} ->
{wik002, Pwd,_Email,Time,_Who,TxtStr,Files,_Patches} =
bin_to_wik002(Root,FileDir,Bin),
Wik = wiki_split:str2wiki(TxtStr),
DeepStr = wiki_to_html:format_wiki(Page, Wik, Root),
DeepFiles = wiki_to_html:format_wiki_files(
Page, FileDir, Files, Root),
Locked = Pwd /= "",
wiki_templates:template(Page, Root,
[DeepStr,
DeepFiles],
utils:time_to_string(Time),
Locked);
_ ->
NewSid = session_new(initial_page_content()),
redirect_create(Page, NewSid, Prefix)
end
end.
thumb_name(File) ->
Extension = filename:extension(File),
(filename:basename(File, Extension)++"_wiki_thb"++Extension).
create_thumb_if_needed(SrcPath, DstPath) ->
case thumb_up2date(SrcPath, DstPath) of
true ->
ok;
false ->
create_thumb(SrcPath, DstPath)
end.
thumb_up2date(Src,Thumb) ->
DstAge = get_age(Thumb),
SrcAge = get_age(Src),
DstAge > SrcAge.
create_thumb(SrcPath, DstPath) ->
os:cmd("convert -size 90x90 -scale 90x90 '"++
shell_quote(SrcPath)++"' '"++
shell_quote(DstPath)++"'").
getThumb(Params, Root, Prefix) ->
Page = getnode(Params),
Pict = getopt("pict", Params),
if
Page == undefined ->
html_error(invalid_request);
true ->
{_WobFile, FileDir} = page2filename(Page, Root),
ThumbName = thumb_name(Pict),
SrcPath = Root ++ "/" ++ FileDir ++ "/" ++ Pict,
DstPath = Root ++ "/" ++ FileDir ++ "/" ++ ThumbName,
UpToDate = thumb_up2date(SrcPath, DstPath),
if UpToDate ->
done;
true ->
create_thumb(SrcPath, DstPath)
end,
{redirect_local, Prefix++FileDir++"/"++ThumbName}
end.
getMidSize(Params, Root, Prefix) ->
Page = getnode(Params),
Pict = getopt("pict", Params),
if
Page == undefined ->
html_error(invalid_request);
true ->
{_WobFile, FileDir} = page2filename(Page, Root),
Extension = filename:extension(Pict),
MidName = (filename:basename(Pict, Extension)++
"_wiki_mid"++Extension),
SrcPath = Root ++ "/" ++ FileDir ++ "/" ++ Pict,
DstPath = Root ++ "/" ++ FileDir ++ "/" ++ MidName,
DstAge = get_age(DstPath),
SrcAge = get_age(SrcPath),
if DstAge > SrcAge -> false;
true ->
os:cmd("convert -size 400x400 -scale 400x400 '"++
shell_quote(SrcPath)++"' '"++
shell_quote(DstPath)++"'")
end,
case file:read_file_info(DstPath) of
{error,_} ->
{redirect_local, Prefix++FileDir++"/"++Pict};
_ ->
{redirect_local, Prefix++FileDir++"/"++MidName}
end
end.
shell_quote([]) ->
[];
shell_quote([$'|R]) ->
[$\\, $'| shell_quote(R)];
shell_quote([C|R]) ->
[C|shell_quote(R)].
get_age(Path) ->
case file:read_file_info(Path) of
{error, _} ->
{{0,0,0},{0,0,0}};
{ok, FileInfo} ->
FileInfo#file_info.mtime
end.
fixupFiles(Params, Root, Prefix) ->
Page = getnode(Params),
if
Page == undefined ->
html_error(invalid_request);
true ->
importFiles(Page, Root, Prefix)
end.
importFiles(Page, Root, Prefix) ->
{WobFile, FileDir} = page2filename(Page, Root),
case file:read_file(WobFile) of
{ok, Bin} ->
{wik002, Pwd,Email,Time,Who,TxtStr,Files,Patches} =
bin_to_wik002(Root,FileDir,Bin),
CurFiles = files(Root++"/"++FileDir, "^"),
CurFileNames = [basename(CF) || CF <- CurFiles,
string:str(CF,"_wiki_thb")==0,
string:str(CF,"_wiki_mid")==0],
F = fun(Fn) ->
case lists:keysearch(Fn,2,Files) of
{value, File} ->
File;
false ->
{file, Fn, "", []}
end
end,
NewFiles = [F(X) || X <- CurFileNames],
Ds = {wik002, Pwd,Email,Time,Who,TxtStr,NewFiles,Patches},
B = term_to_binary(Ds),
file:write_file(WobFile, B),
redirect({node, Page}, Prefix);
_ ->
NewSid = session_new(initial_page_content()),
redirect_create(Page, NewSid, Prefix)
end.
importFiles([WobFile]) ->
FileDir = wobfile_to_filedir(WobFile),
case file:read_file(WobFile) of
{ok, Bin} ->
{wik002, Pwd, Email, Time, Who, TxtStr, Files, Patches} =
bin_to_wik002(Bin),
CurFiles = files(FileDir, "^"),
CurFileNames = [basename(CF) || CF <- CurFiles],
F = fun(Fn) ->
case lists:keysearch(Fn, 2, Files) of
{value, File} ->
File;
false ->
{file, Fn, "", []}
end
end,
AddedFiles = [tostring(Y)++" " ||
Y <- CurFileNames,
not(lists:keymember(Y,2,Files))],
NewFiles = [F(X) || X <- CurFileNames],
Ds = {wik002, Pwd,Email,Time,Who,TxtStr,NewFiles,Patches},
B = term_to_binary(Ds),
BackupFile = tostring(WobFile)++".bak",
file:write_file(BackupFile, Bin),
io:format("Saved old wob file as ~s\n", [BackupFile]),
file:write_file(WobFile, B),
io:format("Added files: ~s\n", [AddedFiles]),
halt();
_ ->
io:format("Error - failed to read wob file"),
halt()
end.
addFile([WobFile, FileAtm]) ->
addFile([WobFile, FileAtm], halt).
addFile([WobFile, FileAtm], Halt) ->
_FileDir = wobfile_to_filedir(WobFile),
File = tostring(FileAtm),
case file:read_file(WobFile) of
{ok, Bin} ->
{wik002, Pwd, Email, Time, Who, TxtStr, Files, Patches} =
bin_to_wik002(Bin),
NewFiles =
case lists:keysearch(File, 2, Files) of
{value, _} ->
Files;
false ->
[{file, File, "", []}|Files]
end,
Ds = {wik002, Pwd,Email,Time,Who,TxtStr,NewFiles,Patches},
B = term_to_binary(Ds),
BackupFile = tostring(WobFile)++".bak",
file:write_file(BackupFile, Bin),
io:format("Saved old wob file as ~s\n", [BackupFile]),
file:write_file(WobFile, B),
io:format("Added file: ~s\n", [File]);
_ ->
io:format("Error - failed to read wob file")
end,
if Halt == halt -> halt();
true -> ok
end.
createNewPage(Params, Root, Prefix) ->
Page = getnode(Params),
Sid = getopt("sid", Params),
if
Sid /= undefined ->
{Txt,Passwd,Email} =
session_get_all(Sid, initial_page_content(), "", ""),
createNewPage1(Page, Root, Sid, Prefix, Txt, Passwd, Email);
Sid == undefined ->
NewSid = session_new(initial_page_content()),
redirect_create(Page, NewSid, Prefix)
end.
createNewPage1(Page, Root, Sid, _Prefix, Content, Passwd, Email) ->
wiki_templates:template2(
Root,
"New Page",
yaws_api:htmlize(Page),
[p("Creating a new page. "
"If you want a password protected page "
"then fill in both the password fields - otherwise "
"leave them blank."),
p("If you fill in the email field and forget the page password "
"then the system can mail you back the password of the page if "
"you forget it."),
p("Click on 'Preview' when you are ready to store the page."),
form("POST", "previewNewPage.yaws?sid="++str2urlencoded(Sid),
[input("submit", "review", "Preview"),
input("hidden", "node", Page),
hr(),
"<table>\n"
"<tr> <td align=left>Password: </td>",
"<td align=left> ", password_entry("password1", 8, Passwd),
"</td></tr>\n",
"<tr> <td align=left>Reconfirm password: </td>",
"<td align=left> ",password_entry("password2", 8, Passwd),
"</td></tr>\n",
"<tr> <td align=left>Email: </td>",
"<td align=left> ",
input("text","email",Email),
"</td></tr>\n",
"</table>\n",
p(),
textarea("text", 25, 72, Content),
hr()
])],
false).
storePage(Params, Root, Prefix) ->
Password = getopt("password", Params, ""),
Page = getnode(Params),
Cancel = getopt("cancel", Params),
Edit = getopt("edit", Params),
Sid = getopt("sid", Params),
if
Cancel /= undefined ->
session_end(Sid),
redirect({node, Page}, Prefix);
true ->
case checkPassword(Page, Password, Root, Prefix) of
true ->
if
Edit /= undefined ->
Txt0 = getopt("txt", Params),
Txt = zap_cr(urlencoded2str(Txt0)),
session_set_text(Sid, Txt),
redirect_edit(Page, Sid, Password, Prefix);
true ->
storePage1(Params, Root, Prefix)
end;
false ->
show({bad_password, Page}, Root);
error ->
show({no_such_page,Page}, Root)
end
end.
storePage1(Params, Root, Prefix) ->
Page = getnode(Params),
Txt0 = getopt("txt", Params),
Sid = getopt("sid", Params),
Txt = zap_cr(urlencoded2str(Txt0)),
session_end(Sid),
{File,_FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
Wik = {wik002,_Pwd,_Email,_Time,_Who,_OldTxt,_Files,_Patches} =
bin_to_wik002(Bin),
store_ok(Page, Root, Prefix, Txt, Wik);
_ ->
show({no_such_page,Page}, Root)
end.
storeNewPage(Params, Root, Prefix) ->
Page = getnode(Params),
Password = getopt("password", Params),
Email0 = getopt("email", Params),
Txt0 = getopt("txt", Params),
Txt = zap_cr(urlencoded2str(Txt0)),
Email = urlencoded2str(Email0),
%% Check the password
{File,_FileDir} = page2filename(Page, Root),
Time = {date(),time()},
Who = "unknown",
B = term_to_binary({wik002,Password,Email,Time,Who,Txt,[],[]}),
case file:write_file(File, B) of
ok ->redirect({node, Page}, Prefix);
{error, Reason} ->
show({failed_to_create_page,file:format_error(Reason)}, Root)
end.
storeTagged(Params, Root, Prefix) ->
Page = getnode(Params),
Tag = getopt("tag", Params),
Txt0 = getopt("txt", Params),
case catch list_to_integer(Tag) of
{'EXIT', _Reason} ->
show({no_such_tag, Tag}, Root);
ITag when is_integer(ITag) ->
Txt = zap_cr(urlencoded2str(Txt0)),
{File,_FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
Wik = {wik002,_Pwd,_Email,_Time,_Who,OldTxt,_Files,_Patches} =
bin_to_wik002(Bin),
W = wiki_split:str2wiki(OldTxt),
{Type, Old} = wiki_split:getRegion(ITag, W),
W2 = case Type of
open ->
wiki_split:putRegion(ITag, W, Txt);
write_append ->
Time = format_time({date(), time()}),
wiki_split:putRegion(ITag, W,
"''" ++ Time ++ "''\n\n" ++
Txt ++ "\n\n____\n" ++ Old)
end,
Str2 = wiki_split:wiki2str(W2),
store_ok(Page, Root, Prefix, Str2, Wik);
_ ->
show({no_such_page,Page}, Root)
end
end.
storeFiles(Params, Root, Prefix) ->
Page = getnode(Params),
Password = getopt("password", Params),
Add = getopt("add", Params),
Update = getopt("update", Params),
Delete = getopt("delete", Params),
Copy = getopt("copy", Params),
Cancel = getopt("cancel", Params),
case checkPassword(Page, Password, Root, Prefix) of
true ->
if
Add /= undefined ->
addFileInit(Params, Root, Prefix);
Update /= undefined ->
updateFilesInit(Params, Root, Prefix);
Delete /= undefined ->
deleteFilesInit(Params, Root, Prefix);
Copy /= undefined ->
copyFilesInit(Params, Root, Prefix);
Cancel /= undefined ->
redirect({node, Page}, Prefix);
true ->
show({no_such_page, Page}, Root)
end;
false ->
getPassword(Page, Root, Prefix, storeFiles, Params);
error ->
show({no_such_page,Page}, Root)
end.
addFileInit(Params, Root, _Prefix) ->
Page = getnode(Params),
Password = getopt("password", Params),
template2(Root, "Add File", Page,
[form("POST", "addFile.yaws",
[
input("hidden", "node", Page),
input("hidden", "password", Password),
"<table width=\"100%\">",
"<tr><th align=left>Attach new file: </th>",
"<th align=left>",
input("file","attached","30"),"</th></tr>",
"<tr><td colspan=2 align=left>",
input("checkbox","unzip","on",""),
"Upload multiple files using zip archive</th></tr>"
"<tr><th colspan=2 align=left>",
"Description: ","</th></tr>",
"<tr><td colspan=2 align=left>",
textarea("text", 10, 72,""),"</td></tr>",
"</table>",
input("submit", "add", "Add"),
input("button", "Cancel",
"parent.location='editFiles.yaws?node="++
str2urlencoded(Page)++"&password="++
str2urlencoded(Password)++"'")]),
"To upload multiple files at one time place them in a zip archive and upload "
"the zip file and check the 'unzip' checkbox above."
],
false).
-record(addfile, {
root,
prefix,
param,
password,
node,
text,
cancel,
fd,
last,
filename,
unzip
}).
%%% addFile
%%% More than a bit messy due to the chunked arguments :-(
addFile(Arg, Root, Prefix) ->
State = prepare_addFile_state(Arg#arg.state, Root, Prefix),
case yaws_api:parse_multipart_post(Arg) of
{cont, Cont, Res} ->
case addFileChunk(Res, State) of
{done, Result} ->
Result;
{cont, NewState} ->
{get_more, Cont, NewState}
end;
{result, Res} ->
case addFileChunk(Res, State#addfile{last=true}) of
{done, Result} ->
Result;
{cont, _} ->
show({error_on_upload, State#addfile.node}, Root)
end
end.
prepare_addFile_state(undefined, Root, Prefix) ->
#addfile{root=Root, prefix=Prefix};
prepare_addFile_state(State, Root, Prefix) ->
State#addfile{root=Root, prefix=Prefix}.
merge_body(undefined, Data) ->
Data;
merge_body(Acc, New) ->
Acc ++ New.
addFileChunk([{part_body, Data}|Res], State) ->
addFileChunk([{body, Data}|Res], State);
addFileChunk([], State) when State#addfile.last==true,
State#addfile.filename /= undefined,
State#addfile.unzip == "on",
State#addfile.fd /= undefined ->
file:close(State#addfile.fd),
Page = State#addfile.node,
{_File,FileDir} = page2filename(Page, State#addfile.root),
FileName = State#addfile.filename,
case lists:reverse(FileName) of
"piz."++_ ->
UnZipCmd = "cd '"++State#addfile.root++"/"++FileDir++
"'; unzip -j -o -qq '" ++ FileName ++"'; rm -f '" ++
FileName ++ "'",
os:cmd(UnZipCmd);
_ ->
done
end,
{done, {redirect_local, {rel_path, "importFiles.yaws?node="++str2urlencoded(Page)}}};
addFileChunk([], State) when State#addfile.last==true,
State#addfile.filename /= undefined,
State#addfile.fd /= undefined ->
Page = State#addfile.node,
{File,_FileDir} = page2filename(Page, State#addfile.root),
{ok, Bin} = file:read_file(File),
{wik002,Pwd,Email,_,_,Txt,OldFiles,Patches} = bin_to_wik002(Bin),
Description = State#addfile.text,
FileName = State#addfile.filename,
NewFile = {file, FileName, Description, []},
KeptOld = lists:keydelete(FileName, 2, OldFiles),
NewFiles = [NewFile|KeptOld],
Time = {date(), time()},
Who = "unknown",
Ds = {wik002,Pwd, Email,Time,Who,Txt,NewFiles,Patches},
B = term_to_binary(Ds),
file:write_file(File, B),
file:close(State#addfile.fd),
{done, redirect_files(Page, Pwd, State#addfile.prefix)};
% {done, redirect({node, Page}, State#addfile.prefix)};
addFileChunk([], State) when State#addfile.last==true ->
Page = State#addfile.node,
{done, show({error_in_upload, Page}, State#addfile.root)};
addFileChunk([], State) ->
{cont, State};
addFileChunk([{head, {"add", _Opts}}|Res], State ) ->
addFileChunk(Res, State#addfile{param = add});
addFileChunk([{body, _Data}|Res], State) when State#addfile.param == add ->
addFileChunk(Res, State);
addFileChunk([{head, {"node", _Opts}}|Res], State ) ->
addFileChunk(Res, State#addfile{param = node});
addFileChunk([{body, Data}|Res], State) when State#addfile.param == node ->
Node = State#addfile.node,
NewNode = merge_body(Node, Data),
addFileChunk(Res, State#addfile{node = NewNode});
addFileChunk([{head, {"unzip", _Opts}}|Res], State ) ->
addFileChunk(Res, State#addfile{param = unzip});
addFileChunk([{body, Data}|Res], State) when State#addfile.param == unzip ->
Unzip = State#addfile.unzip,
NewUnzip = merge_body(Unzip, Data),
addFileChunk(Res, State#addfile{unzip = NewUnzip});
addFileChunk([{head, {"password", _Opts}}|Res], State) ->
addFileChunk(Res, State#addfile{param = password});
addFileChunk([{body, Data}|Res], State) when State#addfile.param == password ->
Password = State#addfile.password,
NewPW = merge_body(Password, Data),
addFileChunk(Res, State#addfile{password = NewPW});
addFileChunk([{head, {"cancel", _Opts}}|_Res], State) ->
{done, redirect({node, State#addfile.node}, State#addfile.prefix)};
addFileChunk([{head, {"text", _Opts}}|Res], State) ->
addFileChunk(Res, State#addfile{param = text});
addFileChunk([{body, Data}|Res], State) when State#addfile.param == text ->
Text = State#addfile.text,
NewText = merge_body(Text, Data),
addFileChunk(Res, State#addfile{text = NewText});
addFileChunk([{head, {"attached", Opts}}|Res], State) ->
Page = State#addfile.node,
Password = State#addfile.password,
FilePath = getopt(filename, Opts),
FileName = basename(FilePath),
Root = State#addfile.root,
Prefix = State#addfile.prefix,
{_,FileDir} = page2filename(Page, Root),
Valid = check_filename(lists:reverse(FileName)),
if
Valid /= ok ->
{error, Reason} = Valid,
{done, show({illegal_filename, FileName, Reason}, Root)};
FileName == "" ->
{done, show({empty_content, Page}, Root)};
true ->
case checkPassword(Page, Password, Root, Prefix) of
true ->
{ok, Fd} = file:open(Root++"/"++FileDir++"/"++
FileName, write),
addFileChunk(Res, State#addfile{fd=Fd,param=attached,
filename=FileName});
false ->
{done, show({bad_password, Page}, Root)};
error->
{done, show({no_such_page,Page}, Root)}
end
end;
addFileChunk([{body, Data}|Res], State) when State#addfile.param == attached ->
file:write(State#addfile.fd, Data),
addFileChunk(Res, State).
check_filename("sway."++_) ->
{error, "Files ending with .yaws files are not allowed."};
check_filename(FileName) ->
case lists:any(fun(C) -> lists:member(C, FileName) end,
"$\\!%^&*[]~\"'`<>|/") of
true ->
{error,
"Illegal character in file. "
"Please remove all of the following characters "
"$\\!%^&*[]~\"'`<>|/ from the filename."};
false ->
ok
end.
updateFilesInit(Params, Root, Prefix) ->
Page = getnode(Params),
Descriptions = [{lists:nthtail(4,N),S} ||
{N,S,_} <- Params,
lists:prefix("cbt_",N)],
{File,_FileDir} = page2filename(Page, Root),
{ok, Bin} = file:read_file(File),
Wik = bin_to_wik002(Bin),
{wik002,Pwd,Email,_Time, _Who,Txt,OldFiles,Patches} = Wik,
%% Update description field of file entry
UpdateDesc =
fun({file, Fname, OldDesc, []}) ->
case lists:keysearch(Fname, 1, Descriptions) of
{value, {_,NewDesc}} ->
{file, Fname, NewDesc, []};
_ ->
{file, Fname, OldDesc, []}
end;
({file, Fname, _}) ->
case lists:keysearch(Fname, 1, Descriptions) of
{value, {_,NewDesc}} ->
{file, Fname, NewDesc, []};
_ ->
{file, Fname, [], []}
end
end,
NewFiles = [UpdateDesc(F) || F <- OldFiles],
Time = {date(), time()},
Who = "unknown",
Ds = {wik002,Pwd, Email,Time,Who,Txt,NewFiles,Patches},
B = term_to_binary(Ds),
file:write_file(File, B),
redirect_files(Page, Pwd, Prefix).
deleteFilesInit(Params, Root, _Prefix) ->
Page = getnode(Params),
Password = getopt("password", Params),
CheckedFiles = [lists:nthtail(3,N) ||
{N,_,_} <- Params,
lists:prefix("cb_",N)],
{File,FileDir} = page2filename(Page, Root),
{ok, Bin} = file:read_file(File),
_Wik = {wik002,_Pwd,_Email,_Time, _Who,_Txt,Files,_Patches}
= bin_to_wik002(Bin),
Extend = fun({file, Name, Desc, _}) ->
{file, Name, Desc, []};
({file, Name, _}) ->
{file, Name, "", []}
end,
OldFiles = [ Extend(F) || F <- Files],
DelFiles = [ {file, Name, Desc, []} || {file, Name, Desc, _} <- OldFiles,
lists:member(Name, CheckedFiles)],
List =
wiki_to_html:format_wiki_files(Page, FileDir,
lists:keysort(2,DelFiles), Root,
"The following files will be deleted "
"from " ++ Page ++ "."),
DelList = [input("hidden", "del_"++Name, Name) ||
{file, Name, _, _} <- DelFiles],
template2(Root, "Confirm", Page,
[List,
form("POST", "deleteFiles.yaws",
[DelList,
input("submit", "delete", "Delete"),
input("submit", "cancel", "Cancel"),
input("hidden", "node", Page),
input("hidden", "password", Password)])
],
false).
deleteFiles(Params, Root, Prefix) ->
Password = getopt("password", Params, ""),
Page = getnode(Params),
Cancel = getopt("cancel", Params),
if
Cancel /= undefined ->
redirect({node, Page}, Prefix);
true ->
case checkPassword(Page, Password, Root, Prefix) of
true ->
deleteFiles1(Params, Root, Prefix);
false ->
show({bad_password, Page}, Root);
error ->
show({no_such_page,Page}, Root)
end
end.
deleteFiles1(Params, Root, Prefix) ->
Page = getnode(Params),
CheckedFiles = [lists:nthtail(4,N) ||
{N,_,_} <- Params,
lists:prefix("del_",N)],
{File,FileDir} = page2filename(Page, Root),
{ok, Bin} = file:read_file(File),
_Wik = {wik002,Pwd,Email,_Time, _Who,Txt,Files,Patches}
= bin_to_wik002(Bin),
Extend = fun({file, Name, Desc, _}) ->
{file, Name, Desc, []};
({file, Name, _}) ->
{file, Name, "", []}
end,
OldFiles = [ Extend(F) || F <- Files],
DelFiles = [ {file, Name, Desc, []} || {file, Name, Desc, _} <- OldFiles,
lists:member(Name, CheckedFiles)],
NewFiles = [ {file, Name, Desc, []} ||
{file, Name, Desc, _} <- OldFiles,
not lists:member(Name, CheckedFiles)],
Time = {date(), time()},
Who = "unknown",
Ds = {wik002,Pwd, Email,Time,Who,Txt,NewFiles,Patches},
B = term_to_binary(Ds),
lists:foreach(fun({file,F,_,_}) ->
Extension = filename:extension(F),
BaseName = filename:basename(F, Extension),
ThumbName = (BaseName++"_wiki_thb"++Extension),
MidName = (BaseName++"_wiki_mid"++Extension),
file:delete(Root++"/"++FileDir++"/"++ThumbName),
file:delete(Root++"/"++FileDir++"/"++MidName),
file:delete(Root++"/"++FileDir++"/"++F)
end, DelFiles),
file:write_file(File, B),
redirect_files(Page, Pwd, Prefix).
copyFilesInit(Params, Root, Prefix) ->
Page = getnode(Params),
Password = getopt("password", Params),
CheckedFiles = [lists:nthtail(3,N) ||
{N,_,_} <- Params,
lists:prefix("cb_",N)],
{File,FileDir} = page2filename(Page, Root),
{ok, Bin} = file:read_file(File),
_Wik = {wik002,_Pwd,_Email,_Time, _Who,_Txt,Files,_Patches}
= bin_to_wik002(Bin),
Extend = fun({file, Name, Desc, _}) ->
{file, Name, Desc, []};
({file, Name, _}) ->
{file, Name, "", []}
end,
OldFiles = [ Extend(F) || F <- Files],
CpFiles = [ {file, Name, Desc, []} || {file, Name, Desc, _} <- OldFiles,
lists:member(Name, CheckedFiles)],
List =
wiki_to_html:format_wiki_files(Page, FileDir,
lists:keysort(2,CpFiles), Root,
"The following files will be copied "
"from " ++ Page ++ "."),
CpList = [input("hidden", "cp_"++Name, Name) ||
{file, Name, _, _} <- CpFiles],
PageFiles = sort(files(Root, "\\.wob$")),
Pages = [filename:basename(P,".wob") || P <- PageFiles, P /= File],
if
length(CheckedFiles) == 0 ->
editFiles(Params, Root, Prefix);
true ->
template2(Root, "Confirm", Page,
[List,
form("POST", "copyFiles.yaws",
[CpList,
"Destination: ",
input("select","destination",Pages),
input("submit", "copy", "Copy"),
input("submit", "cancel", "Cancel"),
input("hidden", "node", Page),
input("hidden", "password", Password)])
],
false)
end.
copyFiles(Params, Root, Prefix) ->
Password = getopt("password", Params, ""),
Page = getnode(Params),
Cancel = getopt("cancel", Params),
if
Cancel /= undefined ->
redirect({node, Page}, Prefix);
true ->
case checkPassword(Page, Password, Root, Prefix) of
true ->
copyFiles1(Params, Root, Prefix);
false ->
show({bad_password, Page}, Root);
error ->
show({no_such_page,Page}, Root)
end
end.
copyFiles1(Params, Root, Prefix) ->
Page = getnode(Params),
Dest = getopt("destination", Params),
case checkPassword(Dest, "", Root, Prefix) of
true ->
copyFiles3(Params, Root, Prefix);
false ->
getPassword(Dest, Root, Prefix, copyFiles2, Params);
error ->
show({no_such_page,Page}, Root)
end.
copyFiles2(Params, Root, Prefix) ->
Page = getnode(Params),
Dest = getopt("destination", Params),
Password = getopt("password", Params, ""),
case checkPassword(Dest, Password, Root, Prefix) of
true ->
copyFiles3(Params, Root, Prefix);
false ->
show({bad_password, Page}, Root);
error ->
show({no_such_page,Page}, Root)
end.
copyFiles3(Params, Root, Prefix) ->
Page = getnode(Params),
Dest = getopt("destination", Params),
{_SrcWobFile, SrcFileDir} = page2filename(Page, Root),
{_DstWobFile, DstFileDir} = page2filename(Dest, Root),
SrcFileNames = [lists:nthtail(3,N) ||
{N,_S,_} <- Params,
lists:prefix("cp_",N)],
SrcDir = Root ++ "/" ++ SrcFileDir ++ "/",
DstDir = Root ++ "/" ++ DstFileDir ++ "/",
[os:cmd("cp '"++SrcDir++F++"' '"++DstDir++"'") || F <- SrcFileNames],
importFiles(Dest, Root, Prefix).
store_ok(Page, _Root, Prefix, OldTxt,
{wik002,_Pwd,_Email,_Time,_Who,OldTxt,_Files,_Patches}) ->
redirect({node, Page}, Prefix);
store_ok(Page, Root, Prefix, NewTxt,
{wik002,Pwd,Email,_Time,_Who,OldTxt,Files,Patches}) ->
Patch = wiki_diff:diff(NewTxt, OldTxt),
Time = {date(), time()},
Who = "unknown",
Patches1 = [{Patch,Time,Who}|Patches],
Ds = {wik002,Pwd, Email,Time,Who,NewTxt,Files,Patches1},
B = term_to_binary(Ds),
{File,_FileDir} = page2filename(Page, Root),
file:write_file(File, B),
redirect({node, Page}, Prefix).
showHistory(Params, Root, _Prefix) ->
Page = getnode(Params),
{File,_FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
{wik002,_Pwd,_Email,_Time,_Who,_OldTxt,_Files,Patches} =
bin_to_wik002(Bin),
Links = reverse(mk_history_links(reverse(Patches), Page, 1)),
template2(Root, "History", Page, Links, false);
_ ->
show({no_such_page, Page}, Root)
end.
redirect({node, Page}, Prefix) ->
{redirect_local, Prefix++"showPage.yaws?node="++str2urlencoded(Page)}.
redirect_files(Page, Password, Prefix) ->
{redirect_local,
Prefix++"editFiles.yaws?node="++str2urlencoded(Page)++
"&password="++str2urlencoded(Password)}.
redirect_edit(Page, Sid, Password, Prefix) ->
UrlSid = str2urlencoded(Sid),
UrlPW = str2urlencoded(Password),
{redirect_local, Prefix++"editPage.yaws?node="++str2urlencoded(Page)++
"&sid="++UrlSid++"&password="++UrlPW}.
redirect_create(Page, Sid, Prefix) ->
UrlSid = str2urlencoded(Sid),
{redirect_local, Prefix++"createNewPage.yaws?node="++str2urlencoded(Page)++
"&sid="++UrlSid}.
redirect_change(Page, Prefix) ->
{redirect_local, Prefix++"changePassword.yaws?node="++
str2urlencoded(Page)}.
mk_history_links([{C,Time,_Who}|T], Page, N) ->
[["<li>",i2s(N)," modified on <a href='showOldPage.yaws?node=",
str2urlencoded(Page),
"&index=",i2s(N),
"'>",format_time(Time),"</a> size ", i2s(size(C)), " bytes",
"\n"]|mk_history_links(T, Page, N+1)];
mk_history_links([], _, _) ->
[].
format_time({{Year,Month,Day},{Hour,Min,Sec}}) ->
[i2s(Year),"-",i2s(Month),"-",i2s(Day)," ",
i2s(Hour),":",i2s(Min),":",i2s(Sec)].
allPages(_, Root, _Prefix) ->
Files = sort(files(Root, "\\.wob$")),
template2(Root, "All Pages", "All Pages",
[p("This is a list of all pages known to the system."),
lists:map(fun(I) ->
F = filename:basename(I, ".wob"),
[wiki_to_html:format_link(F, Root),
"<br>"]
end,
Files)], false).
lastEdited(_, Root, _Prefix) ->
Files = sort(files(Root, "\\.wob$")),
_S = lists:flatten(lists:map(fun(I) ->
"~" ++ filename:basename(I, ".wob") ++"\n\n"
end, Files)),
V = reverse(sort(
lists:map(fun(I) -> {last_edited_time(I), I} end, Files))),
Groups = group_by_day(V),
S1 = lists:map(fun({{Year,Month,Day},Fx}) ->
[p(),i2s(Year),"-",i2s(Month),"-",i2s(Day),"<p>",
"<ul>",
lists:map(fun(F) ->
F1 = filename:basename(F, ".wob"),
J = wiki_to_html:format_link(F1, Root),
[J,"<br>"] end, Fx),
"</ul>"]
end, Groups),
template2(Root,"Last Edited", "These are the last edited files",
S1, false).
group_by_day([]) ->
[];
group_by_day([{{Day,_Time}, File}|T]) ->
{Stuff, T1} = collect_this_day(Day, T, [File]),
T2 = group_by_day(T1),
[Stuff|T2].
collect_this_day(Day, [{{Day,_Time},File}|T], L) ->
collect_this_day(Day, T, [File|L]);
collect_this_day(Day, T, L) ->
{{Day,reverse(L)}, T}.
last_edited_time(File) ->
case file:read_file(File) of
{ok, Bin} ->
{wik002,_Pwd,_Email,Time,_Who,_Txt,_Files,_Patches} =
bin_to_wik002(Bin),
Time;
_ ->
error
end.
showOldPage(Params, Root, _Prefix) ->
Page = getnode(Params),
Nt = getopt("index", Params),
case catch list_to_integer(Nt) of
{'EXIT', _Reason} ->
show({no_such_index, Nt}, Root);
Index when is_integer(Index) ->
{File,FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
_Wik = {wik002,_Pwd,_Email,_Time,_Who,Txt,Files,Patches} =
bin_to_wik002(Bin),
%% N = #patches to do
N = length(Patches) - Index + 1,
ThePatches = take(N, Patches),
TxtStr = wiki_diff:patchL(Txt, ThePatches),
W = wiki_split:str2wiki(TxtStr),
DeepStr = wiki_to_html:format_wiki(Page, W, Root),
DeepFiles = wiki_to_html:format_wiki_files(
Page, FileDir,Files, Root),
Form = form("POST", "noop.yaws",
[textarea("text", 25, 75, TxtStr)]),
wiki_templates:template2(Root, Page, Page,
[DeepStr,DeepFiles,"<hr>",
Form],
false);
_ ->
show({no_such_page, Page}, Root)
end
end.
take(0, _) -> [];
take(N, [{P,_,_}|T]) -> [P|take(N-1, T)].
deletePage(Params, Root, Prefix) ->
Page = getnode(Params),
Password = getopt("password", Params, ""),
case checkPassword(Page, Password, Root, Prefix) of
true ->
deletePage1(Params, Root, Prefix);
false ->
show({bad_password, Page}, Root);
error ->
show({no_such_page,Page}, Root)
end.
deletePage1(Params, Root, _Prefix) ->
Page = getnode(Params),
Password = getopt("password", Params),
{File,_FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
{wik002, _Pwd,_Email,_Time,_Who,Content,_Files,_Patches} =
bin_to_wik002(Bin),
template2(Root, "Delete", Page,
[p("Reconfirm deleting this page - hit the 'Delete' "
"button to permanently remove the page."),
form("POST", "finalDeletePage.yaws",
[input("submit", "finaldelete", "Delete"),
input("submit", "cancel", "Cancel"),
input("hidden", "node", Page),
input("hidden", "password", Password),
p(),
textarea("text", 25, 75, Content),
p(),
hr()])],
false);
_ ->
show({no_such_page,Page}, Root)
end.
finalDeletePage(Params, Root, Prefix) ->
Page = getnode(Params),
Password = getopt("password", Params, ""),
Cancel = getopt("cancel", Params),
if
Cancel /= undefined ->
redirect({node, Page}, Prefix);
true ->
case checkPassword(Page, Password, Root, Prefix) of
true ->
finalDeletePage1(Params, Root, Prefix);
false ->
show({bad_password, Page}, Root);
error ->
show({no_such_page,Page}, Root)
end
end.
finalDeletePage1(Params, Root, Prefix) ->
Page = getnode(Params),
_Txt0 = getopt("text", Params),
{File,FileDir} = page2filename(Page, Root),
case file:delete(File) of
ok ->
Files = files(Root++"/"++FileDir, "^"),
[file:delete(F) || F <- Files],
file:del_dir(Root++"/"++FileDir),
redirect({node, "home"}, Prefix);
_ ->
wiki_templates:template2(Root, "Error", Page,
[p("Failed to delete page."),
hr()],
false)
end.
getPassword(Page, Root, _Prefix, Target, Values) ->
Vs = [{"target", atom_to_list(Target), []}|
lists:keydelete("password", 1, Values)],
Hidden = [[input("hidden", Name, Value),"\n"] ||
{Name, Value, _} <- Vs],
template2(Root, "Password", Page,
[p("This page is password protected - provide a password "
"and hit the 'Continue' button."),
form("POST", "putPassword.yaws","f",
(Hidden ++
[input("hidden", "target", atom_to_list(Target)),
"Password: ",
password_entry("password",8),
input("submit","continue","Continue"),
input("submit","cancel","Cancel"),
script("document.f.password.focus();")
]
)
)
], false).
putPassword(Params, Root, Prefix) ->
Target = getopt("target", Params, "error"),
Cancel = getopt("cancel", Params),
Page = getnode(Params),
if
Cancel /= undefined ->
redirect({node, Page}, Prefix);
true ->
case Target of
"error" ->
show({no_such_target, Target}, Root);
_ ->
apply(?MODULE,list_to_atom(Target),[Params, Root, Prefix])
end
end.
editPage(Params, Root, Prefix) ->
Password = getopt("password", Params, ""),
Page = getnode(Params),
Sid = getopt("sid", Params),
case checkPassword(Page, Password, Root, Prefix) of
true ->
editPage(Page, Password, Root, Prefix, Sid);
false ->
getPassword(Page, Root, Prefix, editPage, Params);
error ->
show({no_such_page,Page}, Root)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Session manager
%%
-record(sid, {text="", password="", email=""}).
session_server_ensure_started() ->
case whereis(wiki_session_manager) of
undefined ->
Pid = spawn(?MODULE, session_manager_init, [0,[]]),
register(wiki_session_manager, Pid);
_ ->
done
end.
session_manager_init(N,Sessions) ->
process_flag(trap_exit, true),
session_manager(N,Sessions).
% Sessions = [{Pid, Sid}]
session_manager(N,Sessions) ->
receive
{'EXIT', Pid, _} ->
NewS = lists:keydelete(Pid, 1, Sessions),
session_manager(N, NewS);
{Sid, stop} ->
NewS = lists:keydelete(Sid, 2, Sessions),
case lists:keysearch(Sid, 2, Sessions) of
{value, {Pid, Sid}} ->
Pid ! done;
_ -> do_nothing
end,
session_manager(N,NewS);
{new_sid, From, Txt, Passwd, Email} ->
Sid = integer_to_list(N),
Pid = spawn_link(?MODULE, session_proc,
[Sid,#sid{text=Txt,
password=Passwd,
email=Email}]),
From ! {session_id, Sid},
session_manager(N+1, [{Pid, Sid}|Sessions]);
{new_sid, Sid, From, Txt, Passwd, Email} ->
case lists:keysearch(Sid, 2, Sessions) of
{value, _} ->
From ! {session_id, Sid};
_ ->
Pid = spawn_link(?MODULE, session_proc,
[Sid,#sid{text=Txt,
password=Passwd,
email=Email}]),
From ! {session_id, Sid},
session_manager(N, [{Pid, Sid}|Sessions])
end,
session_manager(N, Sessions);
{to_sid, From, Sid, Msg} ->
case lists:keysearch(Sid, 2, Sessions) of
{value, {Pid, Sid}} ->
Pid ! Msg;
_ ->
From ! {unknown_sid, Sid}
end,
session_manager(N, Sessions);
_Unknown ->
session_manager(N, Sessions)
end.
session_proc(Sid,State) ->
receive
stop ->
exit(done);
{set_text, From, NewTxt} ->
From ! {set, Sid},
session_proc(Sid,State#sid{text=NewTxt});
{get_text, From} ->
From ! {text, Sid, State#sid.text},
session_proc(Sid,State);
{set_all, From, NewTxt, NewPW, NewEmail} ->
From ! {set, Sid},
session_proc(Sid,State#sid{text=NewTxt,
password=NewPW,
email=NewEmail});
{get_all, From} ->
From ! {all, Sid,
State#sid.text,
State#sid.password, State#sid.email},
session_proc(Sid,State)
after
3600000 -> %% one hour
exit(timeout)
end.
to_sm(Msg) ->
session_server_ensure_started(),
wiki_session_manager ! Msg.
session_end(undefined) -> done;
session_end(Sid) -> to_sm({to_sid, self(), Sid, stop}).
session_new(Txt) ->
session_new(Txt, "", "").
session_new(Sid, Txt) ->
session_new(Sid, Txt, "", "").
session_new(Txt, Passwd, Email) ->
to_sm({new_sid, self(), Txt, Passwd, Email}),
receive
{session_id, Sid} -> Sid
end.
session_new(Sid, Txt, Passwd, Email) ->
to_sm({new_sid, Sid, self(), Txt, Passwd, Email}),
receive
{session_id, Sid} -> Sid
end.
session_get_text(undefined, OldTxt) -> OldTxt;
session_get_text(Sid, OldTxt) ->
to_sm({to_sid, self(), Sid, {get_text, self()}}),
receive
{text, Sid, Txt} ->
Txt;
{unknown_sid, Sid} ->
session_new(Sid, OldTxt),
OldTxt
end.
session_set_text(undefined, _) -> done;
session_set_text(Sid, Txt) ->
to_sm({to_sid, self(), Sid, {set_text, self(), Txt}}),
receive
{set, Sid} -> done;
{unknown_sid, Sid} ->
session_new(Sid, Txt)
end.
session_get_all(undefined, Txt, Passwd, Email) -> {Txt,Passwd,Email};
session_get_all(Sid, Txt, Passwd, Email) ->
to_sm({to_sid, self(), Sid, {get_all, self()}}),
receive
{all, Sid, OldTxt, OldPasswd, OldEmail} ->
{OldTxt, OldPasswd, OldEmail};
{unknown_sid, Sid} ->
session_new(Sid, Txt, Passwd, Email),
{Txt, Passwd, Email}
end.
session_set_all(undefined, _,_,_) -> done;
session_set_all(Sid, Txt, Passwd, Email) ->
to_sm({to_sid, self(), Sid, {set_all, self(), Txt, Passwd, Email}}),
receive
{set, Sid} -> done;
{unknown_sid, Sid} ->
session_new(Sid, Txt, Passwd, Email)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
editPage(Page, Password, Root, Prefix, Sid) ->
{File,_FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
{wik002, _Pwd,_Email,_Time,_Who,TxtStr,_Files,_Patches} =
bin_to_wik002(Bin),
if
Sid /= undefined ->
OldTxt = session_get_text(Sid, TxtStr),
edit1(Page, Root, Password, OldTxt, Sid);
true ->
NewSid = session_new(TxtStr),
redirect_edit(Page, NewSid, Password, Prefix)
end;
_ ->
show({no_such_page,Page}, Root)
end.
edit1(Page, Root, Password, Content, Sid) ->
template2(Root, "Edit", Page,
[p("Edit this page - when you have finished hit the 'Preview' "
"button to check your results."),
form("POST", "previewPage.yaws?sid="++str2urlencoded(Sid),
"f1",
[textarea("text", 25, 75, Content),
p(),
input("submit", "preview", "Preview"),
input("submit", "delete", "Delete"),
input("submit", "cancel", "Cancel"),
input("submit", "chpasswd", "Password"),
input("hidden", "node", Page),
input("hidden", "password", Password),
hr()])
], false).
sendMeThePassword(Params, Root, _Prefix) ->
Page = getnode(Params),
Email = getopt("email", Params),
{File,_FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
{wik002,Pwd,EmailOwner,_Time,_Who,_OldTxt,_Files,_Patches} =
bin_to_wik002(Bin),
%% io:format("Here Email=~p EMailOwner=~p~n",[Email,EmailOwner]),
case Email of
"" ->
template2(Root, "Error", "Failure",
[p("This page has no associated email address")],
false);
EmailOwner ->
mail(Page, Email, Pwd),
template2(Root, "Ok", "Success",
[p("The password has been mailed to "),
Email,
p("Have a nice day")],
false);
_Other ->
template2(Root, "Error", "Failure",
[p("Incorrect email address")],
false)
end;
_ ->
show({no_such_file,Page}, Root)
end.
checkPassword(Page, Password, Root, _Prefix) ->
{File,_FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
{wik002, Pwd,_Email,_Time,_Who,_TxtStr,_Files,_Patches} =
bin_to_wik002(Bin),
case Pwd of
"" -> true;
Password -> true;
_ -> false
end;
_ ->
error
end.
editFiles(Params, Root, Prefix) ->
Page = getnode(Params),
Password = getopt("password", Params, ""),
case checkPassword(Page, Password, Root, Prefix) of
true ->
editFiles1(Page, Password, Root, Prefix);
false ->
getPassword(Page, Root, Prefix, editFiles, Params);
error ->
show({no_such_page,Page}, Root)
end.
editFiles1(Page, Password, Root, _Prefix) ->
{File,_FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
{wik002,_Pwd,_Email,_Time,_Who,_OldTxt,Files,_Patches} =
bin_to_wik002(Bin),
CheckBoxes =
lists:map(fun({file,Name,Description,_Content}) ->
["<tr><td align=left valign=top>",
input("checkbox","cb_"++Name,"on", ""),
yaws_api:htmlize(Name),
"</td><td width='70%' align=left "
"valign=top>",
textarea("cbt_"++Name, 2, 20, Description),
"</td></tr>\n"];
({file,Name,_Content}) ->
["<tr><td align=left valign=top>",
input("checkbox","cb_"++Name,"on", ""),
yaws_api:htmlize(Name),
"</td><td width='70%' align=left "
"valign=top>",
textarea("cbt_"++Name, 2, 20, ""),
"</td></tr>\n"]
end, lists:keysort(2,Files)),
Check = ["Check files that you want to operate on:",
p(),
"<table cellspacing=10 width = \"100%\">\n",
CheckBoxes,
"</table>\n",
p(),
hr()],
wiki_templates:template2(Root, "Edit", Page,
[form("POST", "storeFiles.yaws",
[Check,
input("submit", "add", "Add"),
input("submit", "update", "Update"),
input("submit", "delete", "Delete"),
input("submit", "copy", "Copy"),
input("submit", "cancel", "Done"),
input("hidden", "node", Page),
input("hidden", "password", Password)
])], false);
_Error ->
show({no_such_page, Page}, Root)
end.
slideShow(Params, Root, Prefix) ->
Page = getnode(Params),
NextArg = getopt("next", Params, undefined),
PrevArg = getopt("prev", Params, undefined),
AutoArg = getopt("auto", Params, undefined),
case {NextArg, PrevArg, AutoArg} of
{undefined, undefined, undefined} ->
nextSlide(1, next, Page, Root, Prefix);
{undefined, undefined, _} ->
Index = case catch list_to_integer(AutoArg) of
{'EXIT', _Reason} -> 1;
Num when is_integer(Num) -> Num
end,
nextSlide(Index, auto, Page, Root, Prefix);
{undefined, _, undefined} ->
Index = case catch list_to_integer(PrevArg) of
{'EXIT', _Reason} -> 1;
Num when is_integer(Num) -> Num
end,
nextSlide(Index, prev, Page, Root, Prefix);
{_, undefined, undefined} ->
Index = case catch list_to_integer(NextArg) of
{'EXIT', _Reason} -> 1;
Num when is_integer(Num) -> Num
end,
nextSlide(Index, next, Page, Root, Prefix)
end.
nextSlide(Index, Direction, Page, Root, Prefix) ->
{File,FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
{wik002, Pwd,_Email,Time,_Who,_TxtStr,Files,_Patches} =
bin_to_wik002(Bin),
case get_img(Index, Direction, lists:keysort(2,Files)) of
false ->
% done
redirect({node, Page}, Prefix);
{ok, NewIndex, PictFile} ->
FileName = element(2, PictFile),
Comment = element(3, PictFile),
Node = str2urlencoded(Page),
DeepStr =
["<table width='100%'>"
"<tr><td valign=middle>"
"<a href=\"slideShow.yaws?node=", Node,"&prev=",
integer_to_list(NewIndex-1),"\">"
"<img align=middle border=0 src='WikiPreferences.files/back.gif' "
"alt='Back' title='Previous Image'>"
"</a> ",
build_slide_list(Node, NewIndex, length(Files)),
"<a href=\"slideShow.yaws?node=",Node,"&next=",
integer_to_list(NewIndex+1),"\">"
"<img align=middle border=0 src='WikiPreferences.files/forward.gif' "
"alt='Next' title='Next Image'>"
"</a> "
"<a href=\"slideShow.yaws?node=",Node,"&auto=",
integer_to_list(NewIndex+1),"\">"
"<img align=middle border=0 src='WikiPreferences.files/kamera.gif' "
"alt='Play' title='Show slideshow'>"
"</a> "
"<a href=\"thumbIndex.yaws?node=",Node,"\">"
"<img align=middle border=0 src='WikiPreferences.files/index.gif' "
"alt='Thumbnails' title='All images'>"
"</a> "
"</td></tr></table>"
"<p><b>",integer_to_list(NewIndex)," - ",
yaws_api:htmlize(Comment),"</b></p><p>",
"<a href=\"",
wiki:str2urlencoded(FileDir), "/",
wiki:str2urlencoded(FileName),"\" target=\"pict\">",
"<img src=\"getMidSize.yaws?node=", Node,"&pict=",
wiki:str2urlencoded(FileName),
"\" alt='",FileName,
"'></a></p>Click on image to enlarge."],
Auto =
if Direction==auto ->
["<META HTTP-EQUIV=\"REFRESH\" "
"CONTENT=\"10; URL=slideShow.yaws?node=",
Node,"&auto=",integer_to_list(NewIndex+1),
"\">"];
true -> []
end,
F1 = add_blanks_nicely(Page),
_TopHeader =
["<a href='showPage.yaws?node=",
str2urlencoded(Page),
"'>",yaws_api:htmlize(F1),"</a>\n"],
Locked = Pwd /= "",
_Link =
wiki_templates:template(Page, Root,
[DeepStr, Auto],
utils:time_to_string(Time),
Locked)
end;
_ ->
show({no_such_page,Page}, Root)
end.
thumbIndex(Params, Root, Prefix) ->
Page = getnode(Params),
{File,FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
{wik002, Pwd,_Email,Time,_Who,_TxtStr,Files,_Patches} =
bin_to_wik002(Bin),
{NumFiles,_} = lists:mapfoldl(
fun(F,N) -> {{element(2,F),N}, N+1} end,
1, lists:keysort(2,Files)),
Pics = [{F,N} || {F,N} <- NumFiles, pict_suffix(F)],
Node = str2urlencoded(Page),
DeepStr =
["<table>",
build_thumb_table(Pics, Node, Prefix, Root, FileDir),
"</table>"],
F1 = add_blanks_nicely(Page),
_TopHeader =
["<a href='showPage.yaws?node=",Node,"'>",
yaws_api:htmlize(F1),"</a>\n"],
Locked = Pwd /= "",
_Link =
wiki_templates:template(Page, Root, DeepStr,
utils:time_to_string(Time),
Locked);
_ ->
show({no_such_page,Page}, Root)
end.
build_thumb_table([], _Node, _Prefix, _Root, _FileDir) -> [];
build_thumb_table(Pics, Node, Prefix, Root, FileDir) ->
build_thumb_rows(Pics, Node, Prefix, Root, FileDir, 0, 5, ["<tr>"]).
build_thumb_rows([], _Node, _Prefix, _Root, _FileDir, _, _, Acc) ->
lists:reverse(["</tr>"|Acc]);
build_thumb_rows(Pics, Node, Prefix, Root, FileDir, X, X, Acc) ->
build_thumb_rows(Pics, Node, Prefix, Root, FileDir, 0, X,
["</tr>\n<tr>"|Acc]);
build_thumb_rows([{P,Num}|Pics], Node, Prefix, Root, FileDir, N, X, Acc) ->
ThumbName = thumb_name(P),
SrcPath = Root++"/"++FileDir++"/"++P,
DstPath = Root++"/"++FileDir++"/"++ThumbName,
create_thumb_if_needed(SrcPath, DstPath),
Img = Prefix++str2urlencoded(FileDir)++"/"++str2urlencoded(ThumbName),
build_thumb_rows(Pics, Node, Prefix, Root, FileDir, N+1, X,
[lists:append(["<td align=\"center\">"
"<a href=\"slideShow.yaws?node=", Node,
"&next=",integer_to_list(Num),"\">"
"<img src='",Img,
"'></a></td>\n"])|Acc]).
build_slide_list(Node, _Index, Nr) when Nr =< 10 ->
Interval = 1,
lists:flatmap(
fun(X) ->
I = integer_to_list(X),
[" <a href=\"slideShow.yaws?node=",Node,"&next=",I,"\">",
I,"</a> "]
end, lists:seq(1,Nr,Interval));
build_slide_list(Node, _Index, Nr) ->
lists:flatmap(
fun(X) ->
I = if X==0 -> "1" ; true -> integer_to_list(X) end,
[" <a href=\"slideShow.yaws?node=",Node,"&next=",I,"\">",
I,"</a> "]
end, lists:seq(0,Nr,5)).
lowercase([C|S]) -> [lowercase(C)|lowercase(S)];
lowercase(C) when C>=$A, C=<$Z -> C+32;
lowercase(C) -> C.
pict_suffix(File) ->
lists:member(lowercase(filename:extension(File)),
[".gif", ".jpeg", ".jpe", ".jpg"]).
get_img(Index, Direction, Files) ->
case catch lists:nth(Index, Files) of
{'EXIT', _} ->
false;
PictFile ->
FileName = element(2, PictFile),
case lowercase(lists:reverse(FileName)) of
"fig."++_ ->
{ok, Index, PictFile};
"gepj."++_ ->
{ok, Index, PictFile};
"gpj."++_ ->
{ok, Index, PictFile};
_Any ->
if Direction == next ->
get_img(Index+1, Direction, Files);
Direction == auto ->
get_img(Index+1, Direction, Files);
true ->
get_img(Index-1, Direction, Files)
end
end
end.
editTag(Params, Root, _Prefix) ->
Page = getnode(Params),
Tag = getopt("tag", Params),
case catch list_to_integer(Tag) of
{'EXIT', _Reason} ->
show({no_such_tag, Tag}, Root);
ITag when is_integer(ITag) ->
{File,_FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
{wik002,_Pwd,_Email,_Time,_Who,OldTxt,_Files,_Patches} =
bin_to_wik002(Bin),
Wik = wiki_split:str2wiki(OldTxt),
{Type, Str} = wiki_split:getRegion(ITag, Wik),
Str1 = case Type of
open -> Str;
write_append -> ""
end,
wiki_templates:template2(Root, "Edit", Page,
[p("Edit this page - when you have finished hit the "
"'Preview' button to check your results."),
form("POST", "previewTagged.yaws",
[input("submit", "review", "preview"),
input("hidden", "node", Page),
input("hidden", "tag", Tag),
p(),
textarea("text", 25, 75, Str1),
p(),
hr()])], false);
_Error ->
show({no_such_page, Page}, Root)
end
end.
changePassword(Params, Root, _Prefix) ->
Page = getnode(Params),
wiki_templates:template2(
Root, "Edit", Page,
[p("Change password setting for page - to remove a password leave "
"the new passwords blank."),
form("POST", "changePassword2.yaws?node="++
str2urlencoded(Page), "f",
["<table>\n"
"<tr> <td align=left> Old password: </td>"
"<td aligh=left> ", password_entry("password", 8),
"</td></tr>\n"
"<tr> <td align=left>New password: </td>"
"<td aligh=left> ", password_entry("password1", 8),
"</td></tr>\n"
"<tr> <td align=left>Reconfirm password: </td>"
"<td aligh=left> ", password_entry("password2", 8),
"</td></tr>\n"
"</table>",
input("submit", "change", "Change"),
script("document.f.password.focus();")
]
)
], false).
changePassword2(Params, Root, Prefix) ->
Page = getnode(Params),
OldPw = getopt("password", Params),
Pw1 = getopt("password1", Params),
Pw2 = getopt("password2", Params),
{File,_FileDir} = page2filename(Page, Root),
case file:read_file(File) of
{ok, Bin} ->
{wik002,Pwd,Email,Time,Who,Txt,Files,Patches} =
bin_to_wik002(Bin),
if
OldPw == Pwd ->
if
Pw1 == Pw2 ->
Ds = {wik002,Pw1,Email,Time,Who,Txt,Files,Patches},
B = term_to_binary(Ds),
file:write_file(File, B),
redirect({node, Page}, Prefix);
true ->
show({password_mismatch, Page}, Root)
end;
true ->
show({bad_password, Page}, Root)
end;
_Error ->
show({no_such_page, Page}, Root)
end.
previewPage(Params, Root, Prefix) ->
Page = getnode(Params),
Cancel = getopt("cancel", Params),
Delete = getopt("delete", Params),
Change = getopt("chpasswd", Params),
Sid = getopt("sid", Params),
if
Cancel /= undefined ->
session_end(Sid),
redirect({node, Page}, Prefix);
Delete /= undefined ->
session_end(Sid),
deletePage(Params, Root, Prefix);
Change /= undefined ->
session_end(Sid),
redirect_change(Page, Prefix);
true ->
previewPage1(Params, Root, Prefix)
end.
previewPage1(Params, Root, _Prefix) ->
Page = getnode(Params),
Password = getopt("password", Params),
Txt0 = getopt("text", Params),
Sid = getopt("sid", Params,"undefined"),
Txt = zap_cr(Txt0),
Wik = wiki_split:str2wiki(Txt),
session_set_text(Sid, Txt),
template2(Root, "Preview", Page,
[p("If this page is ok hit the \"Store\" button "
"otherwise return to the editing phase by clicking the edit "
"button."),
form("POST", "storePage.yaws?sid="++str2urlencoded(Sid),
[input("submit", "store", "Store"),
input("submit", "cancel", "Cancel"),
input("submit", "edit", "Edit"),
input("hidden", "node", Page),
input("hidden", "password", Password),
input("hidden", "txt", str2formencoded(Txt))]),
p(),hr(),h1(Page),
wiki_to_html:format_wiki(Page, Wik, Root, preview)], false).
%% Preview Tagged
%% Tagged stuff is inside comment and append regions
%% We *dont* want any structure here
previewTagged(Params, Root, _Prefix) ->
Page = getnode(Params),
Tag = getopt("tag", Params),
Txt0 = getopt("text", Params),
Txt = zap_cr(Txt0),
%% we want this stuff to *only* be txt
%% io:format("Here previewTagged:~p~n",[Txt]),
case legal_flat_text(Txt) of
true ->
wiki_templates:template2(
Root, "Preview",
p("If this region is ok hit the \"Store\" button "
"otherwise return to the editing phase by clicking "
"the back button in your browser."),
[form("POST", "storeTagged.yaws",
[input("submit", "store", "Store"),
input("hidden", "node", Page),
input("hidden", "tag", Tag),
input("hidden", "txt", str2formencoded(Txt))]),
p(),hr(),
wiki_to_html:format_wiki(Page,{txt,10000,Txt},Root)],
false);
false ->
show({text_contains,'< or >', in_col_1_which_is_illegal}, Root)
end.
%% Flat text is *not* allowed to contain <
legal_flat_text("<" ++ _) -> false;
legal_flat_text(X) -> legal_flat_text1(X).
legal_flat_text1("\n<" ++ _) -> false;
legal_flat_text1("\n>" ++ _) -> false;
legal_flat_text1([_|T]) -> legal_flat_text1(T);
legal_flat_text1([]) -> true.
previewNewPage(Params, Root, _Prefix) ->
Page = getnode(Params),
P1 = getopt("password1", Params),
P2 = getopt("password2", Params),
Email = getopt("email", Params),
Txt0 = getopt("text", Params),
Sid = getopt("sid", Params),
Txt = zap_cr(Txt0),
Wik = wiki_split:str2wiki(Txt),
session_set_text(Sid, Txt),
if
P1 == P2 ->
session_set_all(Sid,Txt,P1,Email),
template2(Root, "Preview",
p("If this page is ok hit the \"Store\" button "
"otherwise return to the editing phase by clicking "
"the back button in your browser."),
[form("POST", "storeNewPage.yaws",
[input("submit", "store", "Store"),
input("hidden", "node", Page),
input("hidden", "password", P1),
input("hidden", "email", str2formencoded(Email)),
input("hidden", "txt", str2formencoded(Txt))]),
wiki_to_html:format_wiki(Page, Wik, Root)], false);
true ->
show({passwords_differ,P1,P2}, Root)
end.
zap_cr([$\r,$\n|T]) -> [$\n|zap_cr(T)];
zap_cr([H|T]) -> [H|zap_cr(T)];
zap_cr([]) -> [].
wikiZombies(_, Root, _Prefix) ->
wiki_utils:zombies(Root).
allRefsToMe(Params, Root, _Prefix) ->
Page = getnode(Params),
wiki_utils:findallrefsto(Page, Root).
mail(Page, Email, Pwd) ->
send(Email,"Wiki password",
"The password of " ++ Page ++ " is " ++ Pwd ++ "\n").
send(To, Subject, Data) ->
{TmpFile, S} = open_tmp_file("/tmp", ".mail"),
io:format(S, "To: <~s>~n", [To]),
io:format(S, "Subject: ~s~n~n", [Subject]),
io:format(S, "~s~n",[Data]),
io:format(S, ".~nquit~n", []),
file:close(S),
io:format("sending ...~n", []),
os:cmd("/usr/sbin/sendmail -t > /dev/null < " ++ TmpFile),
file:delete(TmpFile).
open_tmp_file(RootName, Suffix) ->
open_tmp_file(10, RootName, Suffix).
open_tmp_file(0, _, Suffix) ->
exit({cannot_open_a_temporay_file, Suffix});
open_tmp_file(N, RootName, Suffix) ->
{_,_,M} = erlang:now(),
FileName = RootName ++ "/" ++ integer_to_list(M) ++ Suffix,
%% io:format("trying to open:~p~n", [FileName]),
case file:open(FileName, write) of
{ok, Stream} ->
{FileName, Stream};
{error, _} ->
open_tmp_file(N-1, RootName, Suffix)
end.
ls(Root) ->
Files = files(Root, "\\.wob$"),
lists:map(fun(I) -> filename:basename(I, ".wob") end, Files).
%%
page2filename(Page, Root) ->
{Root ++ "/" ++ Page ++ ".wob", Page ++ ".files"}.
%%
html_error(invalid_request) ->
{html, "invalid request"}.
%%
bin_to_wik002(Bin) ->
case binary_to_term(Bin) of
{wik001,Pwd,Email,Time,Who,OldTxt,Patches} ->
{wik002,Pwd,Email,Time,Who,OldTxt,[],Patches};
{wik002,Pwd,Email,Time,Who,OldTxt,Files,Patches} ->
{wik002,Pwd,Email,Time,Who,OldTxt,Files,Patches}
end.
bin_to_wik002(Root, FileDir, Bin) ->
%% First check filedir for files, if empty use files from wob
Wik = bin_to_wik002(Bin),
case get_wiki_files(Root, FileDir) of
[] ->
{wik002,_,_,_,_,_,Files,_} = Wik,
create_wiki_files(Root, FileDir, Files);
_ ->
ok
end,
Wik.
%%
get_wiki_files(Root, FileDir) ->
Dir = Root ++ "/" ++ FileDir,
files(Dir, "^").
%%
create_wiki_files(Root, FileDir, Files) ->
Dir = Root ++ "/" ++ FileDir,
file:make_dir(Dir),
F = fun({file,F,_D,C}) ->
file:write_file(Dir++"/"++F, C);
({file,F,C}) ->
file:write_file(Dir++"/"++F, C)
end,
lists:foreach(F, Files),
ok.
%%
password_entry(Name, Size) ->
["<INPUT TYPE=password name=", yaws_api:htmlize(Name)," SIZE=", i2s(Size),">\n"].
password_entry(Name, Size, Value) ->
["<INPUT TYPE=password name=", yaws_api:htmlize(Name)," SIZE=", i2s(Size),
" Value=\"", Value, "\">\n"].
input(Type="button", Name, OnClick) ->
["<INPUT TYPE=",Type," Value=\"",yaws_api:htmlize(Name),"\" onClick=\"", yaws_api:htmlize(OnClick), "\">\n"];
input(Type="file", Name, Size) ->
["<INPUT TYPE=",Type," Name=\"",yaws_api:htmlize(Name),"\" Size=\"", yaws_api:htmlize(Size), "\">\n"];
input(Type="checkbox", Name, Value) ->
["<INPUT TYPE=",Type," Name=\"",yaws_api:htmlize(Name),"\" Value=\"", yaws_api:htmlize(Value),
"\" checked>\n"];
input("select", Name, Values) ->
Options = ["<option>" ++ yaws_api:htmlize(Option) ++ "</option>" || Option <- Values],
["<select Name=\"",yaws_api:htmlize(Name),"\">\n", Options,
"</select>\n"];
input(Type, Name, Value) ->
["<INPUT TYPE=",yaws_api:htmlize(Type)," Name=\"",yaws_api:htmlize(Name),"\" Value=\"", yaws_api:htmlize(Value), "\">\n"].
input(Type="checkbox", Name, Value, State) ->
["<INPUT TYPE=",Type," Name=\"",yaws_api:htmlize(Name),"\" Value=\"", yaws_api:htmlize(Value),
"\" " ++ yaws_api:htmlize(State) ++ ">\n"];
input(Type, Name, Value, Size) ->
["<INPUT TYPE=",yaws_api:htmlize(Type)," Name=\"",yaws_api:htmlize(Name),"\" Value=\"", yaws_api:htmlize(Value),"\"",
"Size=\"",yaws_api:htmlize(Size), "\">\n"].
script(Script) ->
["<script>\n", Script, "\n</script>\n"].
form(Method, Action, Args) ->
["<FORM METHOD=", Method,
" ENCTYPE=\"multipart/form-data\"",
" ACTION=\"", yaws_api:htmlize(Action), "\">",
Args, "</form>\n"].
form(Method, Action, Name, Args) ->
["<FORM METHOD=", Method,
" ENCTYPE=\"multipart/form-data\"",
" ACTION=\"", yaws_api:htmlize(Action), "\" NAME=\"", yaws_api:htmlize(Name), "\">",
Args, "</form>\n"].
textarea(Name, Row, Txt) ->
["<textarea name=\"", yaws_api:htmlize(Name), "\" rows=", i2s(Row),
" wrap=virtual>", yaws_api:htmlize(Txt), "</textarea>\n"].
textarea(Name, Row, Cols, Txt) ->
["<textarea name=\"", yaws_api:htmlize(Name), "\" rows=", i2s(Row),
" cols=", i2s(Cols), " wrap=virtual>",
yaws_api:htmlize(Txt), "</textarea>\n"].
h1(X) -> ["<h1>",yaws_api:htmlize(X),"</h1>"].
b(X) -> ["<b>",yaws_api:htmlize(X),"</b>"].
p() -> "<p>".
p(X) -> ["<p>", yaws_api:htmlize(X), "</p>\n"].
br() -> ["<br>\n"].
hr() -> ["<hr>\n"].
body(X) -> ["<body bgcolor=\"", yaws_api:htmlize(X), "\">"].
pre(X) -> ["<pre>",yaws_api:htmlize(X),"</pre>"].
i2s(I) -> integer_to_list(I).
initial_page_content() -> "\nEnter your text here\n".
bgcolor(C) ->
["<body bgcolor='", yaws_api:htmlize(C), "'>\n"].
top_header(Page) ->
F1 = add_blanks_nicely(Page),
["<h1><a href='allRefsToMe.yaws?node=",str2urlencoded(Page),
"'>",yaws_api:htmlize(F1),"</a></h1>\n"].
add_blanks_nicely([H1,H2|T]) ->
case {little_letter(H1),
big_letter(H2)} of
{true,true} ->
[H1,$ ,H2|add_blanks_nicely(T)];
_ ->
[H1|add_blanks_nicely([H2|T])]
end;
add_blanks_nicely([H|T]) ->
[H|add_blanks_nicely(T)];
add_blanks_nicely([]) ->
[].
big_letter(H) when $A =< H, H =< $Z -> true;
big_letter() -> true;
big_letter() -> true;
big_letter() -> true;
big_letter(_) -> false.
little_letter(H) when $a =< H, H =< $z -> true;
little_letter() -> true;
little_letter() -> true;
little_letter() -> true;
little_letter(_) -> false.
show({bad_password, Page}, Root) ->
template2(Root, "Error", "Incorrect password",
[p("You have supplied an incorrect password"),
p("To find out the the password fill "
"in your email address and click on "
"\"Show password\". If you are "
"the registered owner of this page "
"then I will tell you the password."),
form("POST", "sendMeThePassword.yaws",
[input("hidden", "node", Page),
"email address:",
input("text", "email", ""),
input("submit", "send",
"Show password")])
], false);
show({illegal_filename, FileName, Reason}, Root) ->
template2(Root, "Error", "Illegal filename",
[p("You have supplied an illegal filename: " ++ FileName ++ "."),
p(Reason)],
false);
show(X, _Root) ->
{html, [body("white"),"<pre>",
yaws_api:htmlize(lists:flatten(io_lib:format("~p~n",[X]))),
"</pre>"]}.
show_error(Str) ->
{html, [body("white"),"<pre>","Error: ",Str,"</pre>"]}.
%%----------------------------
%% Utilities
%% Notes on the encoding of URI's
%% This comes from secition 8.2.1 of RFC1866
%% The default encoding for all forms is `application/x-www-form-urlencoded'.
%% A form data set is represented in this media type as follows:
%%
%% 1. The form field names and values are escaped: space characters are
%% replaced by `+', and then reserved characters are escaped as per [URL];
%% that is, non-alphanumeric characters are replaced by `%HH', a percent
%% sign and two hexadecimal digits representing the ASCII code of the
%% character. Line breaks, as in multi-line text field values, are
%% represented as CR LF pairs, i.e. `%0D%0A'.
%%
%% 2. The fields are listed in the order they appear in the document with the
%% name separated from the value by `=' and the pairs separated from each
%% other by `&'. Fields with null values may be omitted. In particular,
%% unselected radio buttons and checkboxes should not appear in the
%% encoded data, but hidden fields with VALUE attributes present should.
%%
str2fileencoded([$_|T]) ->
[$_|str2fileencoded(T)];
str2fileencoded([$ |T]) ->
[$ |str2fileencoded(T)];
str2fileencoded([$.|T]) ->
[$.|str2fileencoded(T)];
str2fileencoded([H|T]) ->
case is_alphanum(H) of
true ->
[H|str2fileencoded(T)];
false ->
{Hi,Lo} = byte2hex(H),
[$%,Hi,Lo|str2fileencoded(T)]
end;
str2fileencoded([]) -> [].
fileencoded2str(Str) ->
urlencoded2str(Str).
str2urlencoded([$\n|T]) ->
"%0D%0A" ++ str2urlencoded(T);
str2urlencoded([$.|T]) ->
"." ++ str2urlencoded(T);
str2urlencoded([H|T]) ->
case is_alphanum(H) of
true ->
[H|str2urlencoded(T)];
false ->
{Hi,Lo} = byte2hex(H),
[$%,Hi,Lo|str2urlencoded(T)]
end;
str2urlencoded([]) -> [].
str2formencoded([$ |T]) ->
[$+|str2formencoded(T)];
str2formencoded([$.|T]) ->
[$.|str2formencoded(T)];
str2formencoded([$\n|T]) ->
"%0D%0A" ++ str2formencoded(T);
str2formencoded([H|T]) ->
case is_alphanum(H) of
true ->
[H|str2formencoded(T)];
false ->
{Hi,Lo} = byte2hex(H),
[$%,Hi,Lo|str2formencoded(T)]
end;
str2formencoded([]) -> [].
byte2hex(X) ->
{nibble2hex(X bsr 4), nibble2hex(X band 15)}.
nibble2hex(X) when X < 10 -> $0 + X;
nibble2hex(X) -> $A + X - 10.
is_alphanum(X) when $0 =< X, X =< $9 -> true;
is_alphanum(X) when $a =< X, X =< $z -> true;
is_alphanum(X) when $A =< X, X =< $Z -> true;
is_alphanum(_) -> false.
urlencoded2str([$%,Hi,Lo|T]) -> [decode_hex(Hi, Lo)|urlencoded2str(T)];
urlencoded2str([$+|T]) -> [$ |urlencoded2str(T)];
urlencoded2str([H|T]) -> [H|urlencoded2str(T)];
urlencoded2str([]) -> [].
%% decode_hex %%
decode_hex(Hex1, Hex2) ->
hex2dec(Hex1)*16 + hex2dec(Hex2).
hex2dec(X) when X >=$0, X =<$9 -> X-$0;
hex2dec($A) -> 10;
hex2dec($B) -> 11;
hex2dec($C) -> 12;
hex2dec($D) -> 13;
hex2dec($E) -> 14;
hex2dec($F) -> 15;
hex2dec($a) -> 10;
hex2dec($b) -> 11;
hex2dec($c) -> 12;
hex2dec($d) -> 13;
hex2dec($e) -> 14;
hex2dec($f) -> 15.
%%
%% MR: Is this really necessary ?
%% We can now use filelib:fold_files/5
files(Dir, Re) ->
case re:compile(Re) of
{ok, Re1} ->
find_files(Dir, Re1, []);
_ ->
[]
end.
find_files(Dir, Re, L) ->
case file:list_dir(Dir) of
{ok, Files} -> find_files(Files, Dir, Re, L);
{error, _} -> L
end.
find_files([File|T], Dir, Re, L) ->
FullName = Dir ++ [$/|File],
case file_type(FullName) of
regular ->
case re:run(FullName, Re) of
{match, _} ->
find_files(T, Dir, Re, [FullName|L]);
_ ->
find_files(T, Dir, Re, L)
end;
_ ->
find_files(T, Dir, Re, L)
end;
find_files([], _, _, L) ->
L.
file_type(File) ->
case file:read_file_info(File) of
{ok, Facts} ->
case element(3, Facts) of
regular -> regular;
directory -> directory;
_ -> error
end;
_ ->
error
end.
%%
read_page(Page, Root) ->
{File,_FileDir} = page2filename(Page, Root),
%% io:format("Reading:~p~n",[Page]),
case file:read_file(File) of
{ok, Bin} ->
{wik002,_Pwd,_Email,_Time,_Who,OldTxt,_Files,_Patches} =
bin_to_wik002(Bin),
{ok, OldTxt};
_ ->
error
end.
%%
mime_type(Name) ->
case lists:reverse(Name) of
"cod."++_ ->
"application/octet-stream";
"piz."++_ ->
"application/x-zip";
"lmth."++_ ->
"text/html";
"mth."++_ ->
"text/html";
"fdp."++_ ->
"application/pdf";
_ ->
"application/octet-stream"
end.
%%
%% Applies the Funs in the list to the Args. Return the value of the
%% first Fun that returns a non-ok value, otherwise ok.
check_precon([],_Args) -> ok;
check_precon([F|Fs], Args) ->
case apply(F,Args) of
ok ->
check_precon(Fs, Args);
NotOk ->
NotOk
end.
%%
getnode(KeyList) ->
case getopt("node", KeyList) of
Node when is_list(Node)->
case re:run(Node, "^[0-9A-Za-z]+$") of
{match, _} ->
Node;
_ ->
undefined
end;
_ ->
undefined
end.
getopt(Key, KeyList) ->
getopt(Key, KeyList, undefined).
% getopt(Key, KeyList, Default) when atom (Key) ->
% getopt(atom_to_list(Key), KeyList, Default);
getopt(Key, KeyList, Default) ->
case lists:keysearch(Key, 1, KeyList) of
false ->
Default;
{value, Tuple} ->
Val = element(2,Tuple),
if
Val == undefined -> Default;
true -> Val
end
end.
getopt_options(Key, KeyList) when is_atom(Key) ->
getopt_options(atom_to_list(Key), KeyList);
getopt_options(Key, KeyList) ->
case lists:keysearch(Key, 1, KeyList) of
{value, Tuple} when size(Tuple) >= 3 ->
element(3,Tuple);
_ ->
undefined
end.
%
basename(FilePath) ->
case string:rchr(FilePath, $\\) of
0 ->
%% probably not a DOS name
filename:basename(FilePath);
N ->
%% probably a DOS name, remove everything after last \
basename(string:substr(FilePath, N+1))
end.
%
getPassword([File]) ->
case file:read_file(File) of
{ok, Bin} ->
{wik002,Pwd,_Email,_Time,_Who,_OldTxt,_Files,_Patches} =
bin_to_wik002(Bin),
io:format("Password is: '~s'\n", [Pwd]),
halt();
_ ->
io:format("Error - failed to open ~s\n", [File]),
halt()
end.
%%
tostring(A) when is_atom(A) ->
atom_to_list(A);
tostring(S) ->
S.
%
wobfile_to_filedir(Wob) ->
WobFile = tostring(Wob),
string:substr(WobFile, 1, length(WobFile)-3)++"files".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
forms(Method, Action, Args) ->
["<FORM METHOD=", Method,
" ACTION=\"", Action, "\">",
Args, "</form>\n"].
searchPara([Para|Paralist]) ->
case Para of
{"search", undefined, []} ->
[];
{"search", Search, []} ->
case re:compile(Search) of
{ok, _RE} ->
Search;
{error, Error} ->
{error, Error}
end;
_Other ->
searchPara(Paralist)
end;
searchPara([]) ->
[].
searchPages(SearchPost, Root, _Prefix) ->
case searchPara(SearchPost) of
[] ->
S0 = [forms("POST", "searchPage.yaws",
[input("submit", "searchstart", "Search"),
input("text", "search", "")])],
template2(Root,"Search", "Wiki page search",
[S0, "<hr>",
h1("Nothing to find")],
false);
{error, Error} ->
S0 = [forms("POST", "searchPage.yaws",
[input("submit", "searchstart", "Search"),
input("text", "search", "")])],
template2(Root,"Search", "Wiki page search",
[S0, "<hr>",
h1(io_lib:format("Error in the regular "
"expression \"~p\"",
[Error]))],
false);
Search ->
Files = sort(files(Root, "\\.wob$")),
{Sres, _S} = lists:mapfoldl(fun(F, S) ->
{searchPage(F, S), S} end,
Search, Files),
Sres_sort = reverse(sort(Sres)),
S1 = lists:map(fun({Matches, Fx}) ->
[p(),i2s(Matches)," ",
wiki_to_html:format_link(
filename:basename(Fx, ".wob"), Root),
"</p>"]
end, Sres_sort),
S0 = [forms("POST", "searchPage.yaws",
[input("submit", "searchstart", "Search"),
input("text", "search", Search)])],
template2(Root,"Search", "Wiki page search",
[S0, "<hr>",
h1("Number of matches found per page"), S1],
false)
end.
searchPage(File, Search) ->
case file:read_file(File) of
{ok, Bin} ->
{wik002,_Pwd,_Email,_Time,_Who,Txt,_Files,_Patches} =
bin_to_wik002(Bin),
{match, Matches} = re:run(Txt, Search),
{length(Matches), File};
_ ->
io:format("Error - failed to open ~s\n", [File]),
{-1, File}
end.
Jump to Line
Something went wrong with that request. Please try again.