Skip to content

Commit

Permalink
import
Browse files Browse the repository at this point in the history
  • Loading branch information
Joony committed May 13, 2009
0 parents commit c359662
Show file tree
Hide file tree
Showing 53 changed files with 2,047 additions and 0 deletions.
17 changes: 17 additions & 0 deletions Emakefile
@@ -0,0 +1,17 @@
{ './src/*', [
{ i, "./include" },
{ outdir, "./ebin" },
debug_info
]}.

{ './src/*/*', [
{ i, "./include" },
{ outdir, "./ebin" },
debug_info
]}.

{ './src/*/*/*', [
{ i, "./include" },
{ outdir, "./ebin" },
debug_info
]}.
5 changes: 5 additions & 0 deletions Makefile
@@ -0,0 +1,5 @@
compile:
erl -make

clean:
rm -rf ./ebin/*.beam
8 changes: 8 additions & 0 deletions include/config.inc
@@ -0,0 +1,8 @@
-ifndef(config_inc).
-define(config_inc, ok).


-record(users, { username, email_address, password, date_joined, last_logged_in, verified=false }).
-record(verification_codes, { email_address, verification_code }).

-endif.
Empty file added include/wf.inc
Empty file.
175 changes: 175 additions & 0 deletions src/db_users.erl
@@ -0,0 +1,175 @@
%% Copyright 2009 Joony (jonathan.mcallister@gmail.com)
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% db_users.erl
%%
%% This module provides all the functions for dealing with users.
%%

-module(db_users).
-include("wf.inc").
-include("config.inc").
-export([init/0, add_user/3, validate_user/2, delete_user/1, is_username_used/1, is_email_used/1, get_email_address/1, verify_email/2, update_verification_code/2, delete_verification_code/1]).

-include_lib("stdlib/include/qlc.hrl").

%%% initialize the database and tables
init() ->
mnesia:start(),
mnesia:delete_table(verification_codes),
mnesia:create_table(verification_codes, [{attributes, record_info (fields, verification_codes)}, {disc_copies, [node()]}]),
mnesia:delete_table(users),
mnesia:create_table(users, [{attributes, record_info (fields, users)}, {disc_copies, [node()]}]),
mnesia:stop().

%%% add the user to the users database
add_user(Username, EmailAddress, Password) ->
<<PasswordDigest:160>> = crypto:sha(Password),
Code = string_utils:generate_random_string(32),
UsersRow = #users { username=Username, email_address=EmailAddress, password=PasswordDigest, date_joined=erlang:universaltime() },
VerificationCodeRow = #verification_codes { email_address=EmailAddress, verification_code=Code },
F = fun() ->
mnesia:write(UsersRow),
mnesia:write(VerificationCodeRow)
end,
case mnesia:transaction(F) of
{atomic, Val} ->
case validate_user(Username, Password) of
{valid, _ID} ->
ok;
{aborted, Reason} ->
io:format("Failed to login after registration. Reason: ~s~n", [Reason]),
{aborted, Reason}
end;
{aborted, Reason} ->
io:format ("Added user failed!~nReason: ~s~n", [Reason]),
{aborted, Reason}
end.

validate_user(Username, Password) ->
<<PasswordDigest:160>> = crypto:sha(Password),
case db_utils:do (qlc:q ([X#users.username || X <- mnesia:table(users), check(X#users.username, X#users.email_address, Username), X#users.password == PasswordDigest])) of
fail ->
{aborted, "Not valid"};
Results ->
if length (Results) == 1 ->
%% update the last logged in time
case update_last_logged_in(Username) of
{atomic, ok} ->
{valid, hd(Results)};
{aborted, Val} ->
io:format("Error: Unable to update last_logged_in for user ~s~n", [Username]),
{aborted, "Not valid"}
end;
true ->
{aborted, "Not valid"}
end
end.

update_last_logged_in(Username) ->
F = fun() ->
[E] = mnesia:read(users, Username, write),
Update = E#users{last_logged_in=erlang:universaltime()},
mnesia:write(Update)
end,
mnesia:transaction(F).

delete_verification_code(EmailAddress) ->
F = fun() ->
mnesia:delete(verification_codes, EmailAddress, write)
end,
mnesia:transaction(F).

delete_user(Username) ->
F = fun() ->
mnesia:delete(users, Username, write)
end,
mnesia:transaction(F).

%% not implemented yet, but will be used for the forgot password process if the user has already requested a code.
update_verification_code(EmailAddress) ->
update_verification_code(EmailAddress, "").

update_verification_code(EmailAddress, Code) ->
F = fun() ->
[E] = mnesia:read(verification_codes, EmailAddress, write),
Update = E#verification_codes{verification_code=Code},
mnesia:write(Update)
end,
mnesia:transaction(F).

%% result seems backwards compared to the name
is_username_used(Username) ->
case db_utils:do(qlc:q ([X#users.username || X <- mnesia:table(users), string:equal(X#users.username, Username)])) of
aborted ->
false;
Results ->
if
length(Results) == 1 ->
false;
true ->
true
end
end.

%%% result seems backwards compared to the name
is_email_used(EmailAddress) ->
case db_utils:do(qlc:q ([X#users.email_address || X <- mnesia:table(users), string:equal(X#users.email_address, EmailAddress)])) of
aborted ->
false;
Results ->
if
length(Results) == 1 ->
false;
true ->
true
end
end.

check(Username, EmailAddress, Input) ->
if
Username == Input ; EmailAddress == Input ->
true;
true ->
false
end.

%%% Used to get the email address for Gravatar
get_email_address(Username) ->
db_utils:do(qlc:q([X#users.email_address || X <- mnesia:table(users), X#users.username =:= Username])).


verify_email(EmailAddress, Code) ->
F = fun() ->
Result = qlc:e(qlc:q([X#verification_codes.email_address || X <- mnesia:table(verification_codes), X#verification_codes.email_address =:= EmailAddress, X#verification_codes.verification_code =:= Code])),
io:format("Result: ~s, ~w~n", [Result, length(Result)]),
if
length(Result) == 1 ->
io:format("Found one result~n"),
mnesia:delete(verification_codes, EmailAddress, write),
[User] = qlc:e(qlc:q([X || X <- mnesia:table(users), X#users.email_address =:= EmailAddress])),
io:format("User: ~w~n", [User]),
UserUpdate = User#users{verified=true},
mnesia:write(UserUpdate);
true ->
{aborted, "Error: problem with the validation code"}
end
end,
case mnesia:transaction (F) of
{atomic, Val} ->
Val;
{aborted, Reason} ->
io:format ("Query: ~w aborted.~nReason: ~w~n", [F, Reason]),
aborted
end.
62 changes: 62 additions & 0 deletions src/db_utils.erl
@@ -0,0 +1,62 @@
%% Copyright 2009 Joony (jonathan.mcallister@gmail.com)
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% db_utils.erl
%%
%% This module provides basic functions for interacting with the database.
%%

-module(db_utils).
-include("wf.inc").
-include("config.inc").
-export([init/0, start/0, stop/0, write/1, do/1, get_all_rows/1]).

-include_lib("stdlib/include/qlc.hrl").

%%% initialize the database and tables. Only run once!
init() ->
mnesia:create_schema([node()]),
mnesia:start(),
db_users:init(),
mnesia:stop().

%%% start the database
start() ->
crypto:start(),
mnesia:start().

%%% stop the database
stop() ->
crypto:stop(),
mnesia:stop().

write(Row) ->
F = fun() ->
mnesia:write(Row)
end,
mnesia:transaction(F).

do(Q) ->
F = fun() -> qlc:e(Q) end,
case mnesia:transaction (F) of
{atomic, Val} ->
Val;
{aborted, Reason} ->
io:format("Query: ~w aborted.~nReason: ~w~n", [Q, Reason]),
aborted
end.

get_all_rows(Table) ->
mnesia:transaction(fun() -> qlc:eval(qlc:q([X || X <- mnesia:table(Table)])) end).

13 changes: 13 additions & 0 deletions src/pages/web_index.erl
@@ -0,0 +1,13 @@
-module (web_index).
-include_lib ("nitrogen/include/wf.inc").
-compile(export_all).

main() ->
#template { file="./wwwroot/template.html"}.

title() ->
"web_index".

body() ->
#label{text="web_index body."}.
event(_) -> ok.
51 changes: 51 additions & 0 deletions src/pages/web_users_dashboard.erl
@@ -0,0 +1,51 @@
%% Copyright 2009 Joony (jonathan.mcallister@gmail.com)
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% web_users_dashboard.erl
%%
%% A users homepage. Users will be directed here after logging in.
%%

-module (web_users_dashboard).
-include_lib ("nitrogen/include/wf.inc").
-compile(export_all).

main() ->
case wf:user() of
undefined ->
wf:redirect("login");
_ ->
ok
end,
#template { file="./wwwroot/template.html"}.

title() ->
"web_users_dashboard".

body() ->
Username = wf:user(),
Body = [
#label { text="web_users_dashboard body." },
#gravatar { email=db_users:get_email_address(Username), size="60" },
#br {},
#link { text="Logout", postback=logout },
#br {}
],
wf:render(Body).

event(logout) ->
wf:clear_user(),
wf:redirect ("login");
event(_) ->
ok.
55 changes: 55 additions & 0 deletions src/pages/web_users_login.erl
@@ -0,0 +1,55 @@
%% Copyright 2009 Joony (jonathan.mcallister@gmail.com)
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% web_users_login.erl
%%
%% The login screen.
%%

-module (web_users_login).
-include_lib ("nitrogen/include/wf.inc").
-compile(export_all).

main() ->
#template { file="./wwwroot/template.html"}.

title() ->
"web_users_login".

body() ->

Body = [
#label { text="Login:" },
#textbox { id=username, postback=login, next=password },
#br {},
#label { text="Password:" },
#password { id=password, postback=login, next=submit },
#br {},
#button { id=submit, text="Login", postback=login }
],
wf:wire(submit, username, #validate { validators = [ #is_required { text="Required" }]}),
wf:render(Body).

event(login) ->
case db_users:validate_user(hd(wf:q(username)), hd(wf:q(password))) of
{ valid, _ID } ->
io:format("User: ~s has logged in~n", [wf:q(username)]),
wf:flash("Correct"),
wf:user(hd(wf:q(username))),
wf:redirect("dashboard");
_ ->
wf:flash("Incorrect")
end;

event(_) -> ok.

0 comments on commit c359662

Please sign in to comment.