├── .gitignore ├── COPYING ├── Emakefile ├── Makefile ├── README.txt ├── ebin └── twoorl.app ├── elib ├── rfc4627 │ └── rfc4627.erl └── twitter_client │ ├── twitter_client.erl │ └── twitter_client.hrl ├── migrations ├── 1.sql ├── 2.sql ├── 3.sql ├── 4.sql ├── 5.sql ├── 6.sql ├── 7.sql ├── 8.sql └── 9.sql ├── src ├── bundles │ ├── twoorl_deu.erl │ ├── twoorl_dk.erl │ ├── twoorl_eng.erl │ ├── twoorl_fra.erl │ ├── twoorl_greek.erl │ ├── twoorl_ita.erl │ ├── twoorl_kor.erl │ ├── twoorl_pol.erl │ ├── twoorl_por_br.erl │ ├── twoorl_rus.erl │ ├── twoorl_spa.erl │ ├── twoorl_sv.erl │ └── twoorl_zh_cn.erl ├── components │ ├── api_controller.erl │ ├── api_view.et │ ├── feeds_controller.erl │ ├── feeds_view.et │ ├── followers_controller.erl │ ├── following.erl │ ├── following_controller.erl │ ├── following_view.et │ ├── grid_controller.erl │ ├── grid_view.et │ ├── home_controller.erl │ ├── home_view.et │ ├── html_container_controller.erl │ ├── html_container_view.et │ ├── language_select_controller.erl │ ├── language_select_view.et │ ├── layout_controller.erl │ ├── layout_view.et │ ├── login_controller.erl │ ├── login_view.et │ ├── logout_controller.erl │ ├── main_controller.erl │ ├── main_view.et │ ├── msg.erl │ ├── navbar_controller.erl │ ├── navbar_view.et │ ├── paging_controller.erl │ ├── paging_view.et │ ├── register_controller.erl │ ├── register_view.et │ ├── replies_controller.erl │ ├── replies_view.et │ ├── reply.erl │ ├── settings_controller.erl │ ├── settings_view.et │ ├── stats_controller.erl │ ├── stats_view.et │ ├── timeline_controller.erl │ ├── timeline_view.et │ ├── twoorls_controller.erl │ ├── twoorls_view.et │ ├── ui_msgs_controller.erl │ ├── ui_msgs_view.et │ ├── user_icon_controller.erl │ ├── user_icon_view.et │ ├── user_list_controller.erl │ ├── user_list_view.et │ ├── users_controller.erl │ ├── users_view.et │ └── usr.erl ├── twitter.erl ├── twoorl.erl ├── twoorl.hrl ├── twoorl_app.hrl ├── twoorl_app_controller.erl ├── twoorl_errs.et ├── twoorl_migrations.erl ├── twoorl_snippets.et ├── twoorl_stats.erl ├── twoorl_sup.erl ├── twoorl_twitter.erl └── twoorl_util.erl ├── twoorl.sql └── www └── static ├── bg.gif ├── bg1.jpg ├── style.css └── twoorl.js /.gitignore: -------------------------------------------------------------------------------- 1 | erl_crash.dump 2 | Mnesia.nonode@nohost/ 3 | *.beam 4 | *.access 5 | *.log 6 | #*# 7 | .#* 8 | .DS_Store 9 | twoorl.mnesia/ 10 | twoorl_stats 11 | -------------------------------------------------------------------------------- /Emakefile: -------------------------------------------------------------------------------- 1 | {"src/twoorl.erl", [{outdir, "./ebin"}]}. 2 | {"src/twoorl_sup.erl", [{outdir, "./ebin"}]}. 3 | {"src/twoorl_server.erl", [{outdir, "./ebin"}, 4 | {i, "/Users/yariv/yaws/include"}]}. 5 | {"elib/rfc4627/*", [{outdir, "./ebin"}]}. 6 | {"elib/twitter_client/*", [{outdir, "./ebin"}]}. 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: code 2 | 3 | code: clean 4 | erl -s make all load -s init stop 5 | 6 | clean: 7 | rm -fv ebin/*.beam twoorl.rel twoorl.script twoorl.boot erl_crash.dump *.log *.access 8 | 9 | cleandb: 10 | rm -rfv *.mnesia Mnesia* 11 | 12 | cleandocs: 13 | rm -fv doc/*.html 14 | rm -fv doc/edoc-info 15 | rm -fv doc/*.css 16 | rm -fv doc/*.png -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | To run Twoorl follow the following steps: 2 | 3 | - Get the latest versiona of ErlyWeb (prior to 0.7.1, this would be from trunk) and Yaws 4 | - Install MySQL and create a MySQL database for twoorl. 5 | - Run twoorl.sql to create the Twoorl tables. 6 | - Edit src/twoorl_app.hrl with your appropriate environment variables. 7 | - Compile src/twoorl.erl with 'erlc'. 8 | - Edit yaws.conf to add the ErlyWeb application settings for Twoorl. 9 | - Start Yaws and in the shell, type "twoorl:start()". Alternatively, pass Yaws the parameter "--runmon twoorl". 10 | 11 | Cheers! 12 | Yariv 13 | 14 | To run in embedded mode: 15 | 16 | $ make clean && make 17 | $ erl -sname twoorlapp -setcookie twoorl -mnesia dir "'twoorl.mnesia'" -yaws embedded true -pa ebin -boot start_sasl 18 | 1> [application:start(X) || X <- [inets, crypto, mnesia, twoorl]]. 19 | [ok, ok, ok, ok] 20 | 21 | # Nick Gerakines 22 | -------------------------------------------------------------------------------- /ebin/twoorl.app: -------------------------------------------------------------------------------- 1 | {application, twoorl, [ 2 | {description, "Twoorl is an open-source Twitter clone."}, 3 | {vsn, "0.3"}, 4 | {modules, [twoorl, twoorl_server, twoorl_sup]}, 5 | {registered, [twoorl]}, 6 | {applications, [kernel, stdlib, sasl, crypto, inets, mnesia]}, 7 | {mod, {twoorl, []}}, 8 | {env, [ 9 | {dbconns, [ 10 | {"localhost", "root", "password", "twoorl", 3} 11 | ]}, 12 | {tables, [session]} 13 | ]}, 14 | {start_phases, [ 15 | {mysql, []}, 16 | {mnesia, []}, 17 | {compile, []} 18 | ]} 19 | ]}. 20 | -------------------------------------------------------------------------------- /elib/rfc4627/rfc4627.erl: -------------------------------------------------------------------------------- 1 | %% JSON - RFC 4627 - for Erlang 2 | %%--------------------------------------------------------------------------- 3 | %% @author Tony Garnock-Jones 4 | %% @author LShift Ltd. 5 | %% @copyright 2007, 2008 Tony Garnock-Jones and LShift Ltd. 6 | %% @license 7 | %% 8 | %% Permission is hereby granted, free of charge, to any person 9 | %% obtaining a copy of this software and associated documentation 10 | %% files (the "Software"), to deal in the Software without 11 | %% restriction, including without limitation the rights to use, copy, 12 | %% modify, merge, publish, distribute, sublicense, and/or sell copies 13 | %% of the Software, and to permit persons to whom the Software is 14 | %% furnished to do so, subject to the following conditions: 15 | %% 16 | %% The above copyright notice and this permission notice shall be 17 | %% included in all copies or substantial portions of the Software. 18 | %% 19 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | %% BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | %% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | %% CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | %% SOFTWARE. 27 | %%--------------------------------------------------------------------------- 28 | %% 29 | %% @reference RFC 30 | %% 4627, the JSON RFC 31 | %% 32 | %% @reference JSON in general 33 | %% 34 | %% @reference Joe Armstrong's 36 | %% message describing the basis of the JSON data type mapping that 37 | %% this module uses 38 | %% 39 | %% @doc An implementation of RFC 4627 (JSON, the JavaScript Object Notation) for Erlang. 40 | %% 41 | %% The basic API is comprised of the {@link encode/1} and {@link decode/1} functions. 42 | %% 43 | %% == Data Type Mapping == 44 | %% 45 | %% The data type mapping I've implemented is as per Joe Armstrong's 46 | %% message [http://www.erlang.org/ml-archive/erlang-questions/200511/msg00193.html] - see {@link json()}. 47 | %% 48 | %% == Unicode == 49 | %% 50 | %% When serializing a string, if characters are found with codepoint 51 | %% >127, we rely on the unicode encoder to build the proper byte 52 | %% sequence for transmission. We still use the \uXXXX escape for 53 | %% control characters (other than the RFC-specified specially 54 | %% recognised ones). 55 | %% 56 | %% {@link decode/1} will autodetect the unicode encoding used, and any 57 | %% strings returned in the result as binaries will contain UTF-8 58 | %% encoded byte sequences for codepoints >127. Object keys containing 59 | %% codepoints >127 will be returned as lists of codepoints, rather 60 | %% than being UTF-8 encoded. If you have already transformed the text 61 | %% to parse into a list of unicode codepoints, perhaps by your own use 62 | %% of {@link unicode_decode/1}, then use {@link decode_noauto/1} to 63 | %% avoid redundant and erroneous double-unicode-decoding. 64 | %% 65 | %% Similarly, {@link encode/1} produces text that is already UTF-8 66 | %% encoded. To get raw codepoints, use {@link encode_noauto/1} and 67 | %% {@link encode_noauto/2}. You can use {@link unicode_encode/1} to 68 | %% UTF-encode the results, if that's appropriate for your application. 69 | %% 70 | %% == Differences to the specification == 71 | %% 72 | %% I'm lenient in the following ways during parsing: 73 | %% 74 | %% 81 | 82 | %% @type json() = jsonobj() | jsonarray() | jsonnum() | jsonstr() | true | false | null. An Erlang representation of a general JSON value. 83 | %% @type jsonobj() = {obj, [{jsonkey(), json()}]}. A JSON "object" or "struct". 84 | %% @type jsonkey() = string(). A field-name within a JSON "object". 85 | %% @type jsonarray() = [json()]. A JSON array value. 86 | %% @type jsonnum() = integer() | float(). A JSON numeric value. 87 | %% @type jsonstr() = binary(). A JSON string value. 88 | %% @type byte() = integer(). An integer >=0 and =<255. 89 | 90 | -module(rfc4627). 91 | 92 | -export([mime_type/0, encode/1, decode/1]). 93 | -export([encode_noauto/1, encode_noauto/2, decode_noauto/1]). 94 | -export([unicode_decode/1, unicode_encode/1]). 95 | -export([from_record/3, to_record/3]). 96 | -export([hex_digit/1, digit_hex/1]). 97 | -export([get_field/2, get_field/3, set_field/3]). 98 | -export([equiv/2]). 99 | 100 | %% @spec () -> string() 101 | %% @doc Returns the IANA-registered MIME type for JSON data. 102 | mime_type() -> 103 | "application/json". 104 | 105 | %% @spec (json()) -> [byte()] 106 | %% 107 | %% @doc Encodes the JSON value supplied, first into Unicode 108 | %% codepoints, and then into UTF-8. 109 | %% 110 | %% The resulting string is a list of byte values that should be 111 | %% interpreted as UTF-8 encoded text. 112 | %% 113 | %% During encoding, atoms and binaries are accepted as keys of JSON 114 | %% objects (type {@link jsonkey()}) as well as the usual strings 115 | %% (lists of character codepoints). 116 | encode(X) -> 117 | unicode_encode({'utf-8', encode_noauto(X)}). 118 | 119 | %% @spec (json()) -> string() 120 | %% 121 | %% @doc Encodes the JSON value supplied into raw Unicode codepoints. 122 | %% 123 | %% The resulting string may contain codepoints with value >=128. You 124 | %% can use {@link unicode_encode/1} to UTF-encode the results, if 125 | %% that's appropriate for your application. 126 | %% 127 | %% During encoding, atoms and binaries are accepted as keys of JSON 128 | %% objects (type {@link jsonkey()}) as well as the usual strings 129 | %% (lists of character codepoints). 130 | encode_noauto(X) -> 131 | lists:reverse(encode_noauto(X, [])). 132 | 133 | %% @spec (json(), string()) -> string() 134 | %% 135 | %% @doc As {@link encode_noauto/1}, but prepends reversed text 136 | %% to the supplied accumulator string. 137 | encode_noauto(true, Acc) -> 138 | "eurt" ++ Acc; 139 | encode_noauto(false, Acc) -> 140 | "eslaf" ++ Acc; 141 | encode_noauto(null, Acc) -> 142 | "llun" ++ Acc; 143 | encode_noauto(Str, Acc) when is_binary(Str) -> 144 | Codepoints = xmerl_ucs:from_utf8(Str), 145 | quote_and_encode_string(Codepoints, Acc); 146 | encode_noauto(Str, Acc) when is_atom(Str) -> 147 | quote_and_encode_string(atom_to_list(Str), Acc); 148 | encode_noauto(Num, Acc) when is_number(Num) -> 149 | encode_number(Num, Acc); 150 | encode_noauto({obj, Fields}, Acc) -> 151 | "}" ++ encode_object(Fields, "{" ++ Acc); 152 | encode_noauto(Arr, Acc) when is_list(Arr) -> 153 | "]" ++ encode_array(Arr, "[" ++ Acc). 154 | 155 | encode_object([], Acc) -> 156 | Acc; 157 | encode_object([{Key, Value}], Acc) -> 158 | encode_field(Key, Value, Acc); 159 | encode_object([{Key, Value} | Rest], Acc) -> 160 | encode_object(Rest, "," ++ encode_field(Key, Value, Acc)). 161 | 162 | encode_field(Key, Value, Acc) when is_binary(Key) -> 163 | Codepoints = xmerl_ucs:from_utf8(Key), 164 | encode_noauto(Value, ":" ++ quote_and_encode_string(Codepoints, Acc)); 165 | encode_field(Key, Value, Acc) when is_atom(Key) -> 166 | encode_noauto(Value, ":" ++ quote_and_encode_string(atom_to_list(Key), Acc)); 167 | encode_field(Key, Value, Acc) when is_list(Key) -> 168 | encode_noauto(Value, ":" ++ quote_and_encode_string(Key, Acc)). 169 | 170 | encode_array([], Acc) -> 171 | Acc; 172 | encode_array([X], Acc) -> 173 | encode_noauto(X, Acc); 174 | encode_array([X | Rest], Acc) -> 175 | encode_array(Rest, "," ++ encode_noauto(X, Acc)). 176 | 177 | quote_and_encode_string(Str, Acc) -> 178 | "\"" ++ encode_string(Str, "\"" ++ Acc). 179 | 180 | encode_string([], Acc) -> 181 | Acc; 182 | encode_string([$" | Rest], Acc) -> 183 | encode_string(Rest, [$", $\\ | Acc]); 184 | encode_string([$\\ | Rest], Acc) -> 185 | encode_string(Rest, [$\\, $\\ | Acc]); 186 | encode_string([X | Rest], Acc) when X < 32 orelse X > 127 -> 187 | encode_string(Rest, encode_general_char(X, Acc)); 188 | encode_string([X | Rest], Acc) -> 189 | encode_string(Rest, [X | Acc]). 190 | 191 | encode_general_char(8, Acc) -> [$b, $\\ | Acc]; 192 | encode_general_char(9, Acc) -> [$t, $\\ | Acc]; 193 | encode_general_char(10, Acc) -> [$n, $\\ | Acc]; 194 | encode_general_char(12, Acc) -> [$f, $\\ | Acc]; 195 | encode_general_char(13, Acc) -> [$r, $\\ | Acc]; 196 | encode_general_char(X, Acc) when X > 127 -> [X | Acc]; 197 | encode_general_char(X, Acc) -> 198 | %% FIXME currently this branch never runs. 199 | %% We could make it configurable, maybe? 200 | Utf16Bytes = xmerl_ucs:to_utf16be(X), 201 | encode_utf16be_chars(Utf16Bytes, Acc). 202 | 203 | encode_utf16be_chars([], Acc) -> 204 | Acc; 205 | encode_utf16be_chars([B1, B2 | Rest], Acc) -> 206 | encode_utf16be_chars(Rest, [hex_digit((B2) band 16#F), 207 | hex_digit((B2 bsr 4) band 16#F), 208 | hex_digit((B1) band 16#F), 209 | hex_digit((B1 bsr 4) band 16#F), 210 | $u, 211 | $\\ | Acc]). 212 | 213 | %% @spec (Nibble::integer()) -> char() 214 | %% @doc Returns the character code corresponding to Nibble. 215 | %% 216 | %% Nibble must be >=0 and =<16. 217 | hex_digit(0) -> $0; 218 | hex_digit(1) -> $1; 219 | hex_digit(2) -> $2; 220 | hex_digit(3) -> $3; 221 | hex_digit(4) -> $4; 222 | hex_digit(5) -> $5; 223 | hex_digit(6) -> $6; 224 | hex_digit(7) -> $7; 225 | hex_digit(8) -> $8; 226 | hex_digit(9) -> $9; 227 | hex_digit(10) -> $A; 228 | hex_digit(11) -> $B; 229 | hex_digit(12) -> $C; 230 | hex_digit(13) -> $D; 231 | hex_digit(14) -> $E; 232 | hex_digit(15) -> $F. 233 | 234 | encode_number(Num, Acc) when is_integer(Num) -> 235 | lists:reverse(integer_to_list(Num), Acc); 236 | encode_number(Num, Acc) when is_float(Num) -> 237 | lists:reverse(float_to_list(Num), Acc). 238 | 239 | %% @spec (Input::(binary() | [byte()])) -> ({ok, json(), Remainder} | {error, Reason}) 240 | %% where Remainder = string() 241 | %% Reason = any() 242 | %% 243 | %% @doc Decodes a JSON value from an input binary or string of 244 | %% Unicode-encoded text. 245 | %% 246 | %% Given a binary, converts it to a list of bytes. Given a 247 | %% list/string, interprets it as a list of bytes. 248 | %% 249 | %% Uses {@link unicode_decode/1} on its input, which results in a list 250 | %% of codepoints, and then decodes a JSON value from that list of 251 | %% codepoints. 252 | %% 253 | %% Returns either `{ok, Result, Remainder}', where Remainder is the 254 | %% remaining portion of the input that was not consumed in the process 255 | %% of decoding Result, or `{error, Reason}'. 256 | decode(Bin) when is_binary(Bin) -> 257 | decode(binary_to_list(Bin)); 258 | decode(Bytes) -> 259 | {_Charset, Codepoints} = unicode_decode(Bytes), 260 | decode_noauto(Codepoints). 261 | 262 | %% @spec (Input::string()) -> ({ok, json(), string()} | {error, any()}) 263 | %% 264 | %% @doc As {@link decode/1}, but does not perform Unicode decoding on its input. 265 | %% 266 | %% Expects a list of codepoints - an ordinary Erlang string - rather 267 | %% than a list of Unicode-encoded bytes. 268 | decode_noauto(Bin) when is_binary(Bin) -> 269 | decode_noauto(binary_to_list(Bin)); 270 | decode_noauto(Chars) -> 271 | case catch parse(skipws(Chars)) of 272 | {'EXIT', Reason} -> 273 | %% Reason is usually far too much information, but helps 274 | %% if needing to debug this module. 275 | {error, Reason}; 276 | {Value, Remaining} -> 277 | {ok, Value, skipws(Remaining)} 278 | end. 279 | 280 | %% @spec ([byte()]) -> [char()] 281 | %% 282 | %% @doc Autodetects and decodes using the Unicode encoding of its input. 283 | %% 284 | %% From RFC4627, section 3, "Encoding": 285 | %% 286 | %%
287 | %% JSON text SHALL be encoded in Unicode. The default encoding is 288 | %% UTF-8. 289 | %% 290 | %% Since the first two characters of a JSON text will always be ASCII 291 | %% characters [RFC0020], it is possible to determine whether an octet 292 | %% stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking 293 | %% at the pattern of nulls in the first four octets. 294 | %% 295 | %% 00 00 00 xx UTF-32BE 296 | %% 00 xx 00 xx UTF-16BE 297 | %% xx 00 00 00 UTF-32LE 298 | %% xx 00 xx 00 UTF-16LE 299 | %% xx xx xx xx UTF-8 300 | %%
301 | %% 302 | %% Interestingly, the BOM (byte-order mark) is not mentioned. We 303 | %% support it here by using it to detect our encoding, discarding it 304 | %% if present, even though RFC4627 explicitly notes that the first two 305 | %% characters of a JSON text will be ASCII. 306 | %% 307 | %% If a BOM ([http://unicode.org/faq/utf_bom.html]) is present, we use 308 | %% that; if not, we use RFC4627's rules (as above). Note that UTF-32 309 | %% is the same as UCS-4 for our purposes (but see also 310 | %% [http://unicode.org/reports/tr19/tr19-9.html]). Note that UTF-16 is 311 | %% not the same as UCS-2! 312 | %% 313 | %% Note that I'm using xmerl's UCS/UTF support here. There's another 314 | %% UTF-8 codec in asn1rt, which works on binaries instead of lists. 315 | %% 316 | unicode_decode([0,0,254,255|C]) -> {'utf-32', xmerl_ucs:from_ucs4be(C)}; 317 | unicode_decode([255,254,0,0|C]) -> {'utf-32', xmerl_ucs:from_ucs4le(C)}; 318 | unicode_decode([254,255|C]) -> {'utf-16', xmerl_ucs:from_utf16be(C)}; 319 | unicode_decode([239,187,191|C]) -> {'utf-8', xmerl_ucs:from_utf8(C)}; 320 | unicode_decode(C=[0,0,_,_|_]) -> {'utf-32be', xmerl_ucs:from_ucs4be(C)}; 321 | unicode_decode(C=[_,_,0,0|_]) -> {'utf-32le', xmerl_ucs:from_ucs4le(C)}; 322 | unicode_decode(C=[0,_|_]) -> {'utf-16be', xmerl_ucs:from_utf16be(C)}; 323 | unicode_decode(C=[_,0|_]) -> {'utf-16le', xmerl_ucs:from_utf16le(C)}; 324 | unicode_decode(C=_) -> {'utf-8', xmerl_ucs:from_utf8(C)}. 325 | 326 | %% @spec (EncodingAndCharacters::{Encoding, [char()]}) -> [byte()] 327 | %% where Encoding = 'utf-32' | 'utf-32be' | 'utf-32le' | 'utf-16' | 328 | %% 'utf-16be' | 'utf-16le' | 'utf-8' 329 | %% 330 | %% @doc Encodes the given characters to bytes, using the given Unicode encoding. 331 | %% 332 | %% For convenience, we supply a partial inverse of unicode_decode; If 333 | %% a BOM is requested, we more-or-less arbitrarily pick the big-endian 334 | %% variant of the encoding, since big-endian is network-order. We 335 | %% don't support UTF-8 with BOM here. 336 | unicode_encode({'utf-32', C}) -> [0,0,254,255|xmerl_ucs:to_ucs4be(C)]; 337 | unicode_encode({'utf-32be', C}) -> xmerl_ucs:to_ucs4be(C); 338 | unicode_encode({'utf-32le', C}) -> xmerl_ucs:to_ucs4le(C); 339 | unicode_encode({'utf-16', C}) -> [254,255|xmerl_ucs:to_utf16be(C)]; 340 | unicode_encode({'utf-16be', C}) -> xmerl_ucs:to_utf16be(C); 341 | unicode_encode({'utf-16le', C}) -> xmerl_ucs:to_utf16le(C); 342 | unicode_encode({'utf-8', C}) -> xmerl_ucs:to_utf8(C). 343 | 344 | parse([$" | Rest]) -> %% " emacs balancing 345 | {Codepoints, Rest1} = parse_string(Rest, []), 346 | {list_to_binary(xmerl_ucs:to_utf8(Codepoints)), Rest1}; 347 | parse("true" ++ Rest) -> {true, Rest}; 348 | parse("false" ++ Rest) -> {false, Rest}; 349 | parse("null" ++ Rest) -> {null, Rest}; 350 | parse([${ | Rest]) -> parse_object(skipws(Rest), []); 351 | parse([$[ | Rest]) -> parse_array(skipws(Rest), []); 352 | parse(Chars) -> parse_number(Chars, []). 353 | 354 | skipws([X | Rest]) when X =< 32 -> 355 | skipws(Rest); 356 | skipws(Chars) -> 357 | Chars. 358 | 359 | parse_string(Chars, Acc) -> 360 | case parse_codepoint(Chars) of 361 | {done, Rest} -> 362 | {lists:reverse(Acc), Rest}; 363 | {ok, Codepoint, Rest} -> 364 | parse_string(Rest, [Codepoint | Acc]) 365 | end. 366 | 367 | parse_codepoint([$" | Rest]) -> %% " emacs balancing 368 | {done, Rest}; 369 | parse_codepoint([$\\, Key | Rest]) -> 370 | parse_general_char(Key, Rest); 371 | parse_codepoint([X | Rest]) -> 372 | {ok, X, Rest}. 373 | 374 | parse_general_char($b, Rest) -> {ok, 8, Rest}; 375 | parse_general_char($t, Rest) -> {ok, 9, Rest}; 376 | parse_general_char($n, Rest) -> {ok, 10, Rest}; 377 | parse_general_char($f, Rest) -> {ok, 12, Rest}; 378 | parse_general_char($r, Rest) -> {ok, 13, Rest}; 379 | parse_general_char($/, Rest) -> {ok, $/, Rest}; 380 | parse_general_char($\\, Rest) -> {ok, $\\, Rest}; 381 | parse_general_char($", Rest) -> {ok, $", Rest}; 382 | parse_general_char($u, [D0, D1, D2, D3 | Rest]) -> 383 | Codepoint = 384 | (digit_hex(D0) bsl 12) + 385 | (digit_hex(D1) bsl 8) + 386 | (digit_hex(D2) bsl 4) + 387 | (digit_hex(D3)), 388 | if 389 | Codepoint >= 16#D800 andalso Codepoint < 16#DC00 -> 390 | % High half of surrogate pair 391 | case parse_codepoint(Rest) of 392 | {low_surrogate_pair, Codepoint2, Rest1} -> 393 | [FinalCodepoint] = 394 | xmerl_ucs:from_utf16be(<>), 396 | {ok, FinalCodepoint, Rest1}; 397 | _ -> 398 | exit(incorrect_usage_of_surrogate_pair) 399 | end; 400 | Codepoint >= 16#DC00 andalso Codepoint < 16#E000 -> 401 | {low_surrogate_pair, Codepoint, Rest}; 402 | true -> 403 | {ok, Codepoint, Rest} 404 | end. 405 | 406 | %% @spec (Hexchar::char()) -> integer() 407 | %% @doc Returns the number corresponding to Hexchar. 408 | %% 409 | %% Hexchar must be one of the characters `$0' through `$9', `$A' 410 | %% through `$F' or `$a' through `$f'. 411 | digit_hex($0) -> 0; 412 | digit_hex($1) -> 1; 413 | digit_hex($2) -> 2; 414 | digit_hex($3) -> 3; 415 | digit_hex($4) -> 4; 416 | digit_hex($5) -> 5; 417 | digit_hex($6) -> 6; 418 | digit_hex($7) -> 7; 419 | digit_hex($8) -> 8; 420 | digit_hex($9) -> 9; 421 | 422 | digit_hex($A) -> 10; 423 | digit_hex($B) -> 11; 424 | digit_hex($C) -> 12; 425 | digit_hex($D) -> 13; 426 | digit_hex($E) -> 14; 427 | digit_hex($F) -> 15; 428 | 429 | digit_hex($a) -> 10; 430 | digit_hex($b) -> 11; 431 | digit_hex($c) -> 12; 432 | digit_hex($d) -> 13; 433 | digit_hex($e) -> 14; 434 | digit_hex($f) -> 15. 435 | 436 | finish_number(Acc, Rest) -> 437 | Str = lists:reverse(Acc), 438 | {case catch list_to_integer(Str) of 439 | {'EXIT', _} -> list_to_float(Str); 440 | Value -> Value 441 | end, Rest}. 442 | 443 | parse_number([], _Acc) -> 444 | exit(syntax_error); 445 | parse_number([$- | Rest], Acc) -> 446 | parse_number1(Rest, [$- | Acc]); 447 | parse_number(Rest, Acc) -> 448 | parse_number1(Rest, Acc). 449 | 450 | parse_number1(Rest, Acc) -> 451 | {Acc1, Rest1} = parse_int_part(Rest, Acc), 452 | case Rest1 of 453 | [] -> finish_number(Acc1, []); 454 | [$. | More] -> 455 | {Acc2, Rest2} = parse_int_part(More, [$. | Acc1]), 456 | parse_exp(Rest2, Acc2, false); 457 | _ -> 458 | parse_exp(Rest1, Acc1, true) 459 | end. 460 | 461 | parse_int_part(Chars = [_Ch | _Rest], Acc) -> 462 | parse_int_part0(Chars, Acc). 463 | 464 | parse_int_part0([], Acc) -> 465 | {Acc, []}; 466 | parse_int_part0([Ch | Rest], Acc) -> 467 | case is_digit(Ch) of 468 | true -> parse_int_part0(Rest, [Ch | Acc]); 469 | false -> {Acc, [Ch | Rest]} 470 | end. 471 | 472 | parse_exp([$e | Rest], Acc, NeedFrac) -> 473 | parse_exp1(Rest, Acc, NeedFrac); 474 | parse_exp([$E | Rest], Acc, NeedFrac) -> 475 | parse_exp1(Rest, Acc, NeedFrac); 476 | parse_exp(Rest, Acc, _NeedFrac) -> 477 | finish_number(Acc, Rest). 478 | 479 | parse_exp1(Rest, Acc, NeedFrac) -> 480 | {Acc1, Rest1} = parse_signed_int_part(Rest, if 481 | NeedFrac -> [$e, $0, $. | Acc]; 482 | true -> [$e | Acc] 483 | end), 484 | finish_number(Acc1, Rest1). 485 | 486 | parse_signed_int_part([$+ | Rest], Acc) -> 487 | parse_int_part(Rest, [$+ | Acc]); 488 | parse_signed_int_part([$- | Rest], Acc) -> 489 | parse_int_part(Rest, [$- | Acc]); 490 | parse_signed_int_part(Rest, Acc) -> 491 | parse_int_part(Rest, Acc). 492 | 493 | is_digit($0) -> true; 494 | is_digit($1) -> true; 495 | is_digit($2) -> true; 496 | is_digit($3) -> true; 497 | is_digit($4) -> true; 498 | is_digit($5) -> true; 499 | is_digit($6) -> true; 500 | is_digit($7) -> true; 501 | is_digit($8) -> true; 502 | is_digit($9) -> true; 503 | is_digit(_) -> false. 504 | 505 | parse_object([$} | Rest], Acc) -> 506 | {{obj, lists:reverse(Acc)}, Rest}; 507 | parse_object([$, | Rest], Acc) -> 508 | parse_object(skipws(Rest), Acc); 509 | parse_object([$" | Rest], Acc) -> %% " emacs balancing 510 | {KeyCodepoints, Rest1} = parse_string(Rest, []), 511 | [$: | Rest2] = skipws(Rest1), 512 | {Value, Rest3} = parse(skipws(Rest2)), 513 | parse_object(skipws(Rest3), [{KeyCodepoints, Value} | Acc]). 514 | 515 | parse_array([$] | Rest], Acc) -> 516 | {lists:reverse(Acc), Rest}; 517 | parse_array([$, | Rest], Acc) -> 518 | parse_array(skipws(Rest), Acc); 519 | parse_array(Chars, Acc) -> 520 | {Value, Rest} = parse(Chars), 521 | parse_array(skipws(Rest), [Value | Acc]). 522 | 523 | %% @spec (Record, atom(), [any()]) -> jsonobj() 524 | %% where Record = tuple() 525 | %% 526 | %% @doc Used by the `?RFC4627_FROM_RECORD' macro in `rfc4627.hrl'. 527 | %% 528 | %% Given a record type definiton of ``-record(myrecord, {field1, 529 | %% field})'', and a value ``V = #myrecord{}'', the code 530 | %% ``?RFC4627_FROM_RECORD(myrecord, V)'' will return a JSON "object" 531 | %% with fields corresponding to the fields of the record. The macro 532 | %% expands to a call to the `from_record' function. 533 | from_record(R, _RecordName, Fields) -> 534 | {obj, encode_record_fields(R, 2, Fields)}. 535 | 536 | encode_record_fields(_R, _Index, []) -> 537 | []; 538 | encode_record_fields(R, Index, [Field | Rest]) -> 539 | case element(Index, R) of 540 | undefined -> 541 | encode_record_fields(R, Index + 1, Rest); 542 | Value -> 543 | [{atom_to_list(Field), Value} | encode_record_fields(R, Index + 1, Rest)] 544 | end. 545 | 546 | %% @spec (JsonObject::jsonobj(), DefaultValue::Record, [atom()]) -> Record 547 | %% where Record = tuple() 548 | %% 549 | %% @doc Used by the `?RFC4627_TO_RECORD' macro in `rfc4627.hrl'. 550 | %% 551 | %% Given a record type definiton of ``-record(myrecord, {field1, 552 | %% field})'', and a JSON "object" ``J = {obj, [{"field1", 123}, 553 | %% {"field2", 234}]}'', the code ``?RFC4627_TO_RECORD(myrecord, J)'' 554 | %% will return a record ``#myrecord{field1 = 123, field2 = 234}''. 555 | %% The macro expands to a call to the `to_record' function. 556 | to_record({obj, Values}, Fallback, Fields) -> 557 | list_to_tuple([element(1, Fallback) | decode_record_fields(Values, Fallback, 2, Fields)]). 558 | 559 | decode_record_fields(_Values, _Fallback, _Index, []) -> 560 | []; 561 | decode_record_fields(Values, Fallback, Index, [Field | Rest]) -> 562 | [case lists:keysearch(atom_to_list(Field), 1, Values) of 563 | {value, {_, Value}} -> 564 | Value; 565 | false -> 566 | element(Index, Fallback) 567 | end | decode_record_fields(Values, Fallback, Index + 1, Rest)]. 568 | 569 | %% @spec (JsonObject::jsonobj(), atom()) -> {ok, json()} | not_found 570 | %% @doc Retrieves the value of a named field of a JSON "object". 571 | get_field({obj, Props}, Key) -> 572 | case lists:keysearch(Key, 1, Props) of 573 | {value, {_K, Val}} -> 574 | {ok, Val}; 575 | false -> 576 | not_found 577 | end. 578 | 579 | %% @spec (jsonobj(), atom(), json()) -> json() 580 | %% @doc Retrieves the value of a named field of a JSON "object", or a 581 | %% default value if no such field is present. 582 | get_field(Obj, Key, DefaultValue) -> 583 | case get_field(Obj, Key) of 584 | {ok, Val} -> 585 | Val; 586 | not_found -> 587 | DefaultValue 588 | end. 589 | 590 | %% @spec (JsonObject::jsonobj(), atom(), json()) -> jsonobj() 591 | %% @doc Adds or replaces a named field with the given value. 592 | %% 593 | %% Returns a JSON "object" that contains the new field value as well 594 | %% as all the unmodified fields from the first argument. 595 | set_field({obj, Props}, Key, NewValue) -> 596 | {obj, [{Key, NewValue} | lists:keydelete(Key, 1, Props)]}. 597 | 598 | %% @spec (A::json(), B::json()) -> bool() 599 | %% @doc Tests equivalence of JSON terms. 600 | %% 601 | %% After Bob Ippolito's `equiv' predicate in mochijson. 602 | equiv({obj, Props1}, {obj, Props2}) -> 603 | L1 = lists:keysort(1, Props1), 604 | L2 = lists:keysort(1, Props2), 605 | equiv_sorted_plists(L1, L2); 606 | equiv(A, B) when is_list(A) andalso is_list(B) -> 607 | equiv_arrays(A, B); 608 | equiv(A, B) -> 609 | A == B. 610 | 611 | equiv_sorted_plists([], []) -> true; 612 | equiv_sorted_plists([], _) -> false; 613 | equiv_sorted_plists(_, []) -> false; 614 | equiv_sorted_plists([{K1, V1} | R1], [{K2, V2} | R2]) -> 615 | K1 == K2 andalso equiv(V1, V2) andalso equiv_sorted_plists(R1, R2). 616 | 617 | equiv_arrays([], []) -> true; 618 | equiv_arrays([], _) -> false; 619 | equiv_arrays(_, []) -> false; 620 | equiv_arrays([V1 | R1], [V2 | R2]) -> 621 | equiv(V1, V2) andalso equiv_arrays(R1, R2). 622 | -------------------------------------------------------------------------------- /elib/twitter_client/twitter_client.hrl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2008 Nick Gerakines 2 | %% 3 | %% Permission is hereby granted, free of charge, to any person 4 | %% obtaining a copy of this software and associated documentation 5 | %% files (the "Software"), to deal in the Software without 6 | %% restriction, including without limitation the rights to use, 7 | %% copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | %% copies of the Software, and to permit persons to whom the 9 | %% Software is furnished to do so, subject to the following 10 | %% conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be 13 | %% included in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | %% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | %% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | %% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | %% OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | -record(status, {created_at, id, text, source, 25 | truncated, in_reply_to_status_id, in_reply_to_user_id, 26 | favorited, user}). 27 | 28 | -record(user, {id, name, screen_name, location, description, 29 | profile_image_url, url, protected, followers_count, status, 30 | profile_background_color, profile_text_color, 31 | profile_link_color, profile_sidebar_fill_color, 32 | profile_sidebar_border_color, friends_count, created_at, 33 | favourites_count, utc_offset, time_zone, following, 34 | notifications, statuses_count}). 35 | -------------------------------------------------------------------------------- /migrations/1.sql: -------------------------------------------------------------------------------- 1 | alter table usr add column session_key char(20); 2 | alter table usr add index(session_key); 3 | alter table usr add column twitter_username varchar(30); 4 | alter table usr add column twitter_password varchar(30); 5 | alter table usr add column twitter_enabled tinyint default 0; # 0=true, 1=false 6 | -------------------------------------------------------------------------------- /migrations/2.sql: -------------------------------------------------------------------------------- 1 | alter table msg modify column body text; -------------------------------------------------------------------------------- /migrations/3.sql: -------------------------------------------------------------------------------- 1 | alter table msg modify column body_raw text; -------------------------------------------------------------------------------- /migrations/4.sql: -------------------------------------------------------------------------------- 1 | #0: not sent, 1: pending, 2: sent_successfully, 3: send_failure 2 | alter table msg add column twitter_status tinyint not null default 0; 3 | alter table msg modify column body_raw text not null; 4 | alter table msg modify column body text not null; 5 | 6 | # fix created_on columns to not change on updates 7 | alter table msg modify column created_on timestamp default current_timestamp; 8 | alter table following modify column created_on timestamp default current_timestamp; 9 | alter table reply modify column created_on timestamp default current_timestamp; 10 | alter table usr modify column created_on timestamp default current_timestamp; 11 | -------------------------------------------------------------------------------- /migrations/5.sql: -------------------------------------------------------------------------------- 1 | alter table usr add column gravatar_enabled tinyint unsigned not null 2 | default 0; 3 | 4 | alter table msg add column usr_gravatar_enabled tinyint unsigned not null 5 | default 0; 6 | 7 | alter table msg add column usr_gravatar_id char(32); 8 | -------------------------------------------------------------------------------- /migrations/6.sql: -------------------------------------------------------------------------------- 1 | alter table msg add column body_nolinks text null; -------------------------------------------------------------------------------- /migrations/7.sql: -------------------------------------------------------------------------------- 1 | alter table usr add column background varchar(255) null; -------------------------------------------------------------------------------- /migrations/8.sql: -------------------------------------------------------------------------------- 1 | # "eng", "esp", "rus", "kor", "pol"... 2 | alter table usr add column language char(5) not null default "eng"; 3 | 4 | -------------------------------------------------------------------------------- /migrations/9.sql: -------------------------------------------------------------------------------- 1 | alter table usr add column spammer tinyint not null default 0; 2 | alter table usr add index(spammer); 3 | 4 | alter table msg add column spam tinyint not null default 0; 5 | alter table msg add index(spam); -------------------------------------------------------------------------------- /src/bundles/twoorl_deu.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_deu). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | 22 | language -> <<"Deutsch">>; 23 | 24 | %% layout 25 | login -> <<"anmelden">>; 26 | register -> <<"registrieren">>; 27 | logged_in_as -> <<"angemeldet als">>; 28 | settings -> <<"Einstellungen">>; 29 | logout -> <<"abmelden">>; 30 | get_source -> 31 | <<"Hol dir den " 32 | "Quelltext">>; 33 | 34 | %% navbar 35 | home -> <<"homepage">>; 36 | replies -> <<"antworten">>; 37 | me -> <<"ich">>; 38 | everyone -> <<"alle">>; 39 | 40 | %% login page 41 | login_cap -> <<"Anmeldung">>; 42 | username -> <<"Benutzername">>; 43 | password -> <<"Passwort">>; 44 | not_member -> <<"Noch kein Mitglied?">>; 45 | login_submit -> <<"anmelden">>; 46 | 47 | %% register page 48 | % note: 'username', 'password' and 'Login_cap' are taken from 49 | % login page section 50 | register_cap -> <<"Registrierung">>; 51 | email -> <<"Email">>; 52 | password2 -> <<"Passwort erneut eingeben">>; 53 | already_member -> <<"Bereits registriert?">>; 54 | 55 | %% home page 56 | upto -> <<"Was machst du gerade?">>; 57 | twitter_msg -> <<"automatisch auch bei Twitter einstellen " 58 | "(bei nicht-Antworten)">>; 59 | send -> <<"senden">>; 60 | 61 | %% main page 62 | public_timeline -> <<"öffentliche Zeitleiste">>; 63 | 64 | %% users page 65 | {no_user, Username} -> 66 | [<<"Der Benutzer '">>, Username, <<"' existiert nicht">>]; 67 | {timeline_of, Username} -> 68 | [Username, <<"s Zeitleiste">>]; 69 | following -> <<"verfolgt">>; 70 | followers -> <<"Fans">>; 71 | follow -> <<"verfolgen">>; 72 | unfollow -> <<"nicht mehr verfolgen">>; 73 | 74 | %% friends page 75 | {friends_of, Userlink} -> 76 | [<<"Leute die ">>, Userlink, <<" verfolgt">>]; 77 | {followers_of, Userlink} -> 78 | [Userlink, <<"s Fans">>]; 79 | {no_friends, Username} -> 80 | [Username, <<" folgt Niemandem">>]; 81 | {no_followers, Username} -> 82 | [Username, <<" hat keine Fans">>]; 83 | 84 | 85 | %% settings page 86 | settings_cap -> <<"Einstellungen">>; 87 | use_gravatar -> <<"Gravatar benutzen?">>; 89 | profile_bg -> <<"Profilhintergrundbild">>; 90 | profile_bg_help -> 91 | <<"Bitte gebe die Url für dein Profilhintergrundbild ein " 92 | "(Url freilassen um das voreingestellte Bild zu verwenden):">>; 93 | twitter_help -> 94 | <<"Hier kannst du deine Twitter-Profileinstellungen eingeben " 95 | "damit deine Twoorls automatisch auf Twitter gepostet werden.

" 96 | "Nur twoorls die keine Antworten enthalten (z.B." 97 | "\"@sergey\") werden auf Twitter gepostet.">>; 98 | twitter_username -> <<"Twitter Benutzername:">>; 99 | twitter_password -> <<"Twitter Passwort:">>; 100 | twitter_auto -> <<"Meine Twoorls automatisch auf Twitter posten?">>; 101 | submit -> <<"Abschicken">>; 102 | 103 | %% error messages 104 | {missing_field, Field} -> 105 | [<<"Das Feld ">>, Field, <<" ist erforderlich">>]; 106 | {username_taken, Val} -> 107 | [<<"Der Benutzername '">>, Val, <<"' ist bereits vergeben">>]; 108 | {invalid_username, Val} -> 109 | [<<"Der Benutzername '">>, Val, 110 | <<"' ist ungültig. Bitte nur Buchstaben, Zahlen und den Unterstrich ('_') " 111 | "verwenden">>]; 112 | invalid_login -> 113 | <<"Ungültiger Benutzername or Passwort">>; 114 | {too_short, Field, Min} -> 115 | [<<"Das Feld ">>, Field, <<" ist zu kurz (mindestens ">>, Min, 116 | <<" Zeichen)">>]; 117 | password_mismatch -> 118 | <<"Die Passwörter passen nicht zusammen">>; 119 | twitter_unauthorized -> 120 | <<"Twitter hat die Kombination Benutzername/Passwort die du " 121 | "eingegeben hast abgelehnt">>; 122 | twitter_authorization_error -> 123 | <<"Konnte mich nicht mit Twitter verbinden. Bitte versuche es nochmal später.">>; 124 | {invalid_url, Field} -> 125 | [<<"Das Feld ">>, Field, <<" URL muss mit 'http://' anfangen">>]; 126 | 127 | %% confirmation messages 128 | settings_updated -> 129 | [<<"Ihre Einstellungen wurden erfolgreich aktualisiert">>]; 130 | 131 | %% miscellaneous 132 | {seconds_ago, Val} -> [<<"vor ">>, Val, <<" Sekunden">>]; 133 | {minutes_ago, Val} -> [<<"vor ">>, Val, <<" Minuten">>]; 134 | {hours_ago, Val} -> [<<"vor ">>, Val, <<" Stunden">>]; 135 | {days_ago, Val} -> [<<"vor ">>, Val, <<" Tagen">>] 136 | end. 137 | -------------------------------------------------------------------------------- /src/bundles/twoorl_dk.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_dk). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | 22 | language -> <<"dansk">>; 23 | 24 | %% layout 25 | login -> <<"log ind">>; 26 | register -> <<"ny bruger">>; 27 | logged_in_as -> <<"logget ind som">>; 28 | settings -> <<"indstillinger">>; 29 | logout -> <<"log ud">>; 30 | get_source -> 31 | <<"Få " 32 | "kildekoden">>; 33 | 34 | %% navbar 35 | home -> <<"hjem">>; 36 | replies -> <<"svarer">>; 37 | me -> <<"jeg">>; 38 | everyone -> <<"alle">>; 39 | 40 | %% login page 41 | login_cap -> <<"Log ind">>; 42 | username -> <<"brugernavn">>; 43 | password -> <<"adgangskode">>; 44 | not_member -> <<"Ikke medlem?">>; 45 | login_submit -> <<"log ind">>; 46 | 47 | %% register page 48 | % note: 'username', 'password' and 'Login_cap' are taken from 49 | % login page section 50 | register_cap -> <<"Ny bruger">>; 51 | email -> <<"email">>; 52 | password2 -> <<"gentag adgangskode">>; 53 | already_member -> <<"Allerede medlem?">>; 54 | 55 | %% home page 56 | upto -> <<"Hvad sidder du og gør?">>; 57 | twitter_msg -> <<"Automatisk sending til Twitter aktiv for " 58 | "ikke-svarer">>; 59 | send -> <<"send">>; 60 | 61 | %% main page 62 | public_timeline -> <<"Offentlig tidlinie">>; 63 | 64 | %% users page 65 | {no_user, Username} -> 66 | [<<"Brugeren '">>, Username, <<"' findes ikke">>]; 67 | {timeline_of, Username} -> 68 | [Username, <<"s tidlinie">>]; 69 | following -> <<"følgende">>; 70 | followers -> <<"følgendsvender">>; 71 | follow -> <<"følge">>; 72 | unfollow -> <<"følge ikke">>; 73 | 74 | %% friends page 75 | {friends_of, Userlink} -> 76 | [<<"Folk ">>, Userlink, <<" følges">>]; 77 | {followers_of, Userlink} -> 78 | [Userlink, <<"s følgendsvender">>]; 79 | {no_friends, Username} -> 80 | [Username, <<" følges ingen">>]; 81 | {no_followers, Username} -> 82 | [Username, <<" har ingen følgendsvender">>]; 83 | 84 | 85 | %% settings page 86 | settings_cap -> <<"Indstillinger">>; 87 | use_gravatar -> <<"Brug Gravatar?">>; 89 | profile_bg -> <<"Profil baggrund">>; 90 | profile_bg_help -> 91 | <<"Indføre urlen for dit profilbaggrundbilede" 92 | "(led tom efter for at bruge normal baggrunden):">>; 93 | twitter_help -> 94 | <<"Du kan give dine twitterkonto-detaljer for at have dine twoorls sendt automatisk til Twitter.

" 95 | "Kun disse twoorls uden svarer (e.g." 96 | "\"@sergey\") vil blive sendt til Twitter.">>; 97 | twitter_username -> <<"Twitter brugernavn:">>; 98 | twitter_password -> <<"Twitter adgangskode:">>; 99 | twitter_auto -> <<"Sende mine Twoorls til Twitter automatisk?">>; 100 | submit -> <<"send frem">>; 101 | 102 | %% error messages 103 | {missing_field, Field} -> 104 | [<<"Felden ">>, Field, <<" er obligatorisk">>]; 105 | {username_taken, Val} -> 106 | [<<"Brugeren '">>, Val, <<"' er allerede besat">>]; 107 | {invalid_username, Val} -> 108 | [<<"Brugeren '">>, Val, 109 | <<"' gælder ikke. Accepteres kun bogstaver, ciffer og understreger ('_') ">>]; 110 | invalid_login -> 111 | <<"Ukendt brugernavn eller adgangskode">>; 112 | {too_short, Field, Min} -> 113 | [<<"Felden ">>, Field, <<" er for kort (">>, Min, 114 | <<" bogstaver som minimum)">>]; 115 | password_mismatch -> 116 | <<"Den valgte adgangskode stemmer ikke overens med gentagelsen">>; 117 | twitter_unauthorized -> 118 | <<"Twitter afslog din brugernavn/adgangskode kombination">>; 119 | twitter_authorization_error -> 120 | <<"Kunne forbinde til Twitter ikke. Prøv igen senere.">>; 121 | {invalid_url, Field} -> 122 | [<<"Felden ">>, Field, <<" URL skal begynde med 'http://'">>]; 123 | 124 | %% confirmation messages 125 | settings_updated -> 126 | [<<"Dine indstillinger blev opdateret">>]; 127 | 128 | %% miscellaneous 129 | {seconds_ago, Val} -> [Val, <<" sekunder siden">>]; 130 | {minutes_ago, Val} -> [Val, <<" minutter siden">>]; 131 | {hours_ago, Val} -> [Val, <<" timer siden">>]; 132 | {days_ago, Val} -> [Val, <<" dage siden">>] 133 | end. 134 | 135 | -------------------------------------------------------------------------------- /src/bundles/twoorl_eng.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_eng). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | 22 | language -> <<"english">>; 23 | 24 | %% layout 25 | login -> <<"login">>; 26 | register -> <<"register">>; 27 | logged_in_as -> <<"logged in as">>; 28 | settings -> <<"settings">>; 29 | logout -> <<"logout">>; 30 | get_source -> 31 | <<"Get the " 32 | "source code">>; 33 | 34 | %% navbar 35 | home -> <<"home">>; 36 | replies -> <<"replies">>; 37 | me -> <<"me">>; 38 | everyone -> <<"everyone">>; 39 | 40 | %% login page 41 | login_cap -> <<"Login">>; 42 | username -> <<"username">>; 43 | password -> <<"password">>; 44 | not_member -> <<"Not a member?">>; 45 | login_submit -> <<"login">>; 46 | 47 | %% register page 48 | % note: 'username', 'password' and 'Login_cap' are taken from 49 | % login page section 50 | register_cap -> <<"Register">>; 51 | email -> <<"email">>; 52 | password2 -> <<"re-enter password">>; 53 | already_member -> <<"Already a member?">>; 54 | 55 | %% home page 56 | upto -> <<"What are you up to?">>; 57 | twitter_msg -> <<"Automatic posting to Twitter enabled for " 58 | "non-replies">>; 59 | send -> <<"send">>; 60 | 61 | %% main page 62 | public_timeline -> <<"Public timeline">>; 63 | 64 | %% users page 65 | {no_user, Username} -> 66 | [<<"The user '">>, Username, <<"' doesn't exist">>]; 67 | {timeline_of, Username} -> 68 | [Username, <<"'s timeline">>]; 69 | following -> <<"following">>; 70 | followers -> <<"followers">>; 71 | follow -> <<"follow">>; 72 | unfollow -> <<"unfollow">>; 73 | 74 | %% friends page 75 | {friends_of, Userlink} -> 76 | [<<"People ">>, Userlink, <<" follows">>]; 77 | {followers_of, Userlink} -> 78 | [Userlink, <<"'s followers">>]; 79 | {no_friends, Username} -> 80 | [Username, <<" isn't following anyone">>]; 81 | {no_followers, Username} -> 82 | [Username, <<" has no followers">>]; 83 | 84 | 85 | %% settings page 86 | settings_cap -> <<"Settings">>; 87 | use_gravatar -> <<"Use Gravatar?">>; 89 | profile_bg -> <<"Profile background">>; 90 | profile_bg_help -> 91 | <<"Enter the url for your profile background image " 92 | "(leave blank to use the default background):">>; 93 | twitter_help -> 94 | <<"You may provide your Twitter account details to have " 95 | "your twoorls automatically posted to Twitter.

" 96 | "Only twoorls that don't contain replies (e.g." 97 | "\"@sergey\") will be posted to Twitter.">>; 98 | twitter_username -> <<"Twitter username:">>; 99 | twitter_password -> <<"Twitter password:">>; 100 | twitter_auto -> <<"Automatically post my Twoorls to Twitter?">>; 101 | submit -> <<"submit">>; 102 | 103 | %% error messages 104 | {missing_field, Field} -> 105 | [<<"The ">>, Field, <<" field is required">>]; 106 | {username_taken, Val} -> 107 | [<<"The username '">>, Val, <<"' is taken">>]; 108 | {invalid_username, Val} -> 109 | [<<"The username '">>, Val, 110 | <<"' is invalid. Only letters, numbers and underscore ('_') " 111 | "are accepted">>]; 112 | invalid_login -> 113 | <<"Invalid username or password">>; 114 | {too_short, Field, Min} -> 115 | [<<"The ">>, Field, <<" is too short (">>, Min, 116 | <<" chars minimum)">>]; 117 | password_mismatch -> 118 | <<"The password values didn't match">>; 119 | twitter_unauthorized -> 120 | <<"Twitter rejected the username/password combination you " 121 | "provided">>; 122 | twitter_authorization_error -> 123 | <<"Couldn't connect to Twitter. Please try again later.">>; 124 | {invalid_url, Field} -> 125 | [<<"The ">>, Field, <<" URL must start with 'http://'">>]; 126 | 127 | %% confirmation messages 128 | settings_updated -> 129 | [<<"Your settings have been updated successfully">>]; 130 | 131 | %% miscellaneous 132 | {seconds_ago, Val} -> [Val, <<" seconds ago">>]; 133 | {minutes_ago, Val} -> [Val, <<" minutes ago">>]; 134 | {hours_ago, Val} -> [Val, <<" hours ago">>]; 135 | {days_ago, Val} -> [Val, <<" days ago">>] 136 | end. 137 | -------------------------------------------------------------------------------- /src/bundles/twoorl_fra.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_fra). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | %% layout 22 | login -> <<"Se connecter">>; 23 | register -> <<"créer mon compte Twoorl">>; 24 | logged_in_as -> <<"connecté comme">>; 25 | settings -> <<"configuration">>; 26 | logout -> <<"quitter">>; 27 | get_source -> 28 | <<"Obtenir le " 29 | "code source">>; 30 | 31 | %% navbar 32 | home -> <<"accueuil">>; 33 | replies -> <<"réponses">>; 34 | me -> <<"moi">>; 35 | everyone -> <<"tous">>; 36 | 37 | %% login page 38 | login_cap -> <<"Se connecter">>; 39 | username -> <<"nom d'utilisateur">>; 40 | password -> <<"mot de passe">>; 41 | not_member -> <<"Vous n'avez pas encore votre compte Twoorl?">>; 42 | login_submit -> <<"se connecter">>; 43 | 44 | %% register page 45 | register_cap -> <<"Créer mon compte Twoorl">>; 46 | email -> <<"adresse électronique">>; 47 | password2 -> <<"veuillez confirmer votre mot de passe">>; 48 | already_member -> <<"Vous avez déjà un compte Twoorl?">>; 49 | 50 | %% home page 51 | upto -> <<"Que faîtes-vous?">>; 52 | twitter_msg -> <<"L'envoi automatique à Twitter des messages " 53 | "sans réponses est activé">>; 54 | 55 | %% main page 56 | public_timeline -> <<"Chronologie publique">>; 57 | 58 | %% users page 59 | {no_user, Username} -> 60 | [<<"Le nom d'utilisateur '">>, Username, <<"' n'existe pas">>]; 61 | {timeline_of, Username} -> 62 | [<<"Chronologie de ">>, Username]; 63 | following -> <<"suivis">>; 64 | followers -> <<"suiveurs">>; 65 | follow -> <<"suivre">>; 66 | unfollow -> <<"ne plus suivre">>; 67 | 68 | %% friends page 69 | {friends_of, Userlink} -> 70 | [<<"Gens suivis par ">>, Userlink]; 71 | {followers_of, Userlink} -> 72 | [<<"Suiveurs de ">>, Userlink]; 73 | {no_friends, Username} -> 74 | [Username, <<" ne suit personne">>]; 75 | {no_followers, Username} -> 76 | [Username, <<" n'est suivi par personne">>]; 77 | 78 | 79 | %% settings page 80 | settings_cap -> <<"Configuration">>; 81 | use_gravatar -> <<"Utiliser un Gravatar?">>; 83 | profile_bg -> <<"Fond d'écran associé à mon profil">>; 84 | profile_bg_help -> 85 | <<"Veuillez fournir l'URL de l'image à utiliser comme fond d'écran " 86 | "(vous pouvez laisser ce champ vide pour utiliser l'image par défaut):">>; 87 | twitter_help -> 88 | <<"Vous pouvez fournir votre information de connexion à Twitter pour que vos " 89 | "Twoorls soient automatiquement envoyés à Twitter.

" 90 | "Seuls les Twoorls sans réponses (par exemple, sans " 91 | "\"@sergey\") seront envoyés à Twitter.">>; 92 | twitter_username -> <<"Nom d'utilisateur Twitter:">>; 93 | twitter_password -> <<"Mot de passe Twitter:">>; 94 | twitter_auto -> <<"Voulez-vous automatiquement envoyer vos Twoorls à Twitter?">>; 95 | submit -> <<"envoyer">>; 96 | 97 | %% error messages 98 | {missing_field, Field} -> 99 | [<<"Le champ ">>, Field, <<" est requis.">>]; 100 | {username_taken, Val} -> 101 | [<<"Le nom d'utilisateur '">>, Val, <<"' n'est pas disponible.">>]; 102 | {invalid_username, Val} -> 103 | [<<"Le nom d'utilisateur '">>, Val, 104 | <<"' n'est pas valide. Seulement les lettres, numéros et le symbole de souligné ('_') sont acceptés.">>]; 105 | invalid_login -> 106 | <<"Nom d'utilisateur ou mot de passe invalides.">>; 107 | {too_short, Field, Min} -> 108 | [<<"Le champ ">>, Field, <<" est trop court (">>, Min, 109 | <<" charactères minimum).">>]; 110 | password_mismatch -> 111 | <<"Les mots de passe ne coïncident pas.">>; 112 | twitter_unauthorized -> 113 | <<"Twitter n'a pas accepté le nom d'usager ou le mot de passe fournis.">>; 114 | twitter_authorization_error -> 115 | <<"Échec lors de la connexion à Twitter. Veuillez essayer plus tard.">>; 116 | {invalid_url, Field} -> 117 | [<<"Le champ URL ">>, Field, <<" doit commencer par 'http://'">>]; 118 | 119 | %% confirmation messages 120 | settings_updated -> 121 | [<<"Votre configuration a été mise à jour.">>] 122 | end. 123 | 124 | -------------------------------------------------------------------------------- /src/bundles/twoorl_greek.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_greek). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | 22 | language -> <<"ελληνικά">>; 23 | 24 | %% layout 25 | login -> <<"Είσοδος">>; 26 | register -> <<"Εγγραφή">>; 27 | logged_in_as -> <<"Εγγεγραμμένος ως">>; 28 | settings -> <<"Ρυθμίσεις">>; 29 | logout -> <<"Έξοδος">>; 30 | get_source -> 31 | <<"Κατεβάστε " 32 | "τον πηγαίο κώδικα">>; 33 | 34 | %% navbar 35 | home -> <<"Αρχική Σελίδα">>; 36 | replies -> <<"Απαντήσεις">>; 37 | me -> <<"εγώ">>; 38 | everyone -> <<"όλοι">>; 39 | 40 | %% login page 41 | login_cap -> <<"Είσοδος">>; 42 | username -> <<"Όνομα Χρήστη">>; 43 | password -> <<"Κωδικός">>; 44 | not_member -> <<"Όχι μέλος">>; 45 | login_submit -> <<"Είσοδος">>; 46 | 47 | %% register page 48 | % note: 'username', 'password' and 'Login_cap' are taken from 49 | % login page section 50 | register_cap -> <<"Εγγραφή">>; 51 | email -> <<"email">>; 52 | password2 -> <<"Ξαναεισάγετε τον κωδικό">>; 53 | already_member -> <<"Ήδη μέλος;">>; 54 | 55 | %% home page 56 | upto -> <<"Με τί ασχολείσαι;">>; 57 | twitter_msg -> <<"Αυτόματη αποστολή στο Twitter ενεργοποιημένη για " 58 | "μη-απαντήσεις">>; 59 | send -> <<"Αποστολή">>; 60 | 61 | %% main page 62 | public_timeline -> <<"Δημόσιο χρονοδιάγραμμα">>; 63 | 64 | %% users page 65 | {no_user, Username} -> 66 | [<<"Ο χρήστης '">>, Username, <<"' δεν υπάρχει">>]; 67 | {timeline_of, Username} -> 68 | [<<"Χρονοδιάγραμμα του ">>, Username]; 69 | following -> <<"ακολουθεί">>; 70 | followers -> <<"ακόλουθοι">>; 71 | follow -> <<"ακολούθησε">>; 72 | unfollow -> <<"μην ακολουθείς">>; 73 | 74 | %% friends page 75 | {friends_of, Userlink} -> 76 | [<<"Άνθρωποι που ο ">>, Userlink, <<" ακολουθεί">>]; 77 | {followers_of, Userlink} -> 78 | [<<"Ακόλουθοι του">>, Userlink]; 79 | {no_friends, Username} -> 80 | [Username, <<" δεν ακολουθεί κανέναν">>]; 81 | {no_followers, Username} -> 82 | [Username, <<" δεν έχει ακολούθους">>]; 83 | 84 | 85 | %% settings page 86 | settings_cap -> <<"Ρυθμίσεις">>; 87 | use_gravatar -> <<"Χρησιμοποιήστε τοGravatar?">>; 89 | profile_bg -> <<"Φόντο προφίλ">>; 90 | profile_bg_help -> 91 | <<"Εισάγετε την διεύθυνση για το αρχείο φόντου " 92 | "(αφήστε κενό για να χρησιμοποιηθεί το προεπιλεγμένο φόντο):">>; 93 | twitter_help -> 94 | <<"Μπορείτε να παρέχετε τα στοιχεία του λογαριασμού σας στο Twitter ώστε " 95 | "τα twoorls σας να αποστέλονται αυτόματα στο Twitter.

" 96 | "Μόνο twoorls, τα οποία δεν περιέχουν απαντήσεις (π.χ." 97 | "\"@sergey\") θα αποστέλονται στο Twitter.">>; 98 | twitter_username -> <<"Όνομα χρήστη Twitter:">>; 99 | twitter_password -> <<"Κωδικός χρήστη Twitter:">>; 100 | twitter_auto -> <<"Να αποστέλονται αυτόματα τα Twoorls μου στο Twitter;">>; 101 | submit -> <<"Αποδοχή">>; 102 | 103 | %% error messages 104 | {missing_field, Field} -> 105 | [<<"Το πεδίο ">>, Field, <<" είναι απαραίτητο">>]; 106 | {username_taken, Val} -> 107 | [<<"Το όνομα χρήστη '">>, Val, <<"' είναι κατειλημένο">>]; 108 | {invalid_username, Val} -> 109 | [<<"Το όνομα χρήστη '">>, Val, 110 | <<"' είναι άκυρο. Μόνο γράμματα, αριθμοί και κάτω παύλα ('_') " 111 | "γίνονται αποδεκτά">>]; 112 | invalid_login -> 113 | <<"Εσφαλμένο όνομα χρήστη ή κωδικός">>; 114 | {too_short, Field, Min} -> 115 | [<<"Το πεδίο ">>, Field, <<" είναι πολύ μικρό (">>, Min, 116 | <<" χαρακτήρες τουλάχιστον)">>]; 117 | password_mismatch -> 118 | <<"Οι δύο κωδικοί δεν ταυτίζονται">>; 119 | twitter_unauthorized -> 120 | <<"Το Twitter απέρριψε τον συνδιασμό όνοματος και κωδικού " 121 | "που δώσατε">>; 122 | twitter_authorization_error -> 123 | <<"Αδύνατη η σύνδεση με το Twitter. Παρακαλούμε προσπαθείστε ξανα σε λίγο.">>; 124 | {invalid_url, Field} -> 125 | [<<"Το πεδίο διεύθυνσης ">>, Field, <<" πρέπει να ξεκινά με 'http://'">>]; 126 | 127 | %% confirmation messages 128 | settings_updated -> 129 | [<<"Οι ρυθμίσεις σας ανανεώθηκαν επιτυχώς">>]; 130 | 131 | %% miscellaneous 132 | {seconds_ago, Val} -> [Val, <<" δευτερόλεπτα πριν">>]; 133 | {minutes_ago, Val} -> [Val, <<" λεπτά πριν">>]; 134 | {hours_ago, Val} -> [Val, <<" ώρες πριν">>]; 135 | {days_ago, Val} -> [Val, <<" μέρες πριν">>] 136 | end. 137 | -------------------------------------------------------------------------------- /src/bundles/twoorl_ita.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_ita). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | 22 | language -> <<"italiano">>; 23 | 24 | %% layout 25 | login -> <<"accedi">>; 26 | register -> <<"iscriviti">>; 27 | logged_in_as -> <<"sei entrato come">>; 28 | settings -> <<"configura">>; 29 | logout -> <<"esci">>; 30 | get_source -> 31 | <<"Scarica il " 32 | "codice sorgente">>; 33 | 34 | %% navbar 35 | home -> <<"home">>; 36 | replies -> <<"risposte">>; 37 | me -> <<"miei">>; 38 | everyone -> <<"tutti">>; 39 | 40 | %% login page 41 | login_cap -> <<"Accesso">>; 42 | username -> <<"utente">>; 43 | password -> <<"password">>; 44 | not_member -> <<"Non sei iscritto?">>; 45 | login_submit -> <<"accedi">>; 46 | 47 | %% register page 48 | % note: 'username', 'password' and 'Login_cap' are taken from 49 | % login page section 50 | register_cap -> <<"Iscriviti">>; 51 | email -> <<"email">>; 52 | password2 -> <<"Inserisci di nuovo la password">>; 53 | already_member -> <<"Sei già iscritto?">>; 54 | 55 | %% home page 56 | upto -> <<"Che cosa stai facendo ora?">>; 57 | twitter_msg -> <<"Posta automaticamente su Twitter i " 58 | "messaggi senza risposta">>; 59 | send -> <<"invia">>; 60 | 61 | %% main page 62 | public_timeline -> <<"Cronologia pubblica">>; 63 | 64 | %% users page 65 | {no_user, Username} -> 66 | [<<"L'utente '">>, Username, <<"' non esiste">>]; 67 | {timeline_of, Username} -> 68 | [<<"Cronologia di">>, Username]; 69 | following -> <<"segue">>; 70 | followers -> <<"seguito da">>; 71 | follow -> <<"segui">>; 72 | unfollow -> <<"non seguire più">>; 73 | 74 | %% friends page 75 | {friends_of, Userlink} -> 76 | [Userlink, <<" segue">>]; 77 | {followers_of, Userlink} -> 78 | [Userlink, <<" è seguito da">>]; 79 | {no_friends, Username} -> 80 | [Username, <<" non sta seguendo nessuno">>]; 81 | {no_followers, Username} -> 82 | [Username, <<" non è seguito da nessuno">>]; 83 | 84 | 85 | %% settings page 86 | settings_cap -> <<"Impostazioni">>; 87 | use_gravatar -> <<"Vuoi usare Gravatar?">>; 89 | profile_bg -> <<"Immagine di sfondo del profilo">>; 90 | profile_bg_help -> 91 | <<"Inserisci la url per l'immagine di sfondo del tuo profilo " 92 | "(non riempire se vuoi usare lo sfondo di default):">>; 93 | twitter_help -> 94 | <<"Inserisci i dati del tuo account Twitter in modo " 95 | "che i tuoi twoorls vengano postati automaticamente su Twitter.

" 96 | "Saranno postati su Twitter solo i twoorls senza " 97 | "risposta (ad esempio\"@sergey\").">>; 98 | twitter_username -> <<"Utente twitter:">>; 99 | twitter_password -> <<"Password twitter:">>; 100 | twitter_auto -> <<"Posta automaticamente i miei twoorls su Twitter">>; 101 | submit -> <<"invia">>; 102 | 103 | %% error messages 104 | {missing_field, Field} -> 105 | [<<"Il campo ">>, Field, <<" è obbligatorio">>]; 106 | {username_taken, Val} -> 107 | [<<"Il nome utente '">>, Val, <<"' esiste già">>]; 108 | {invalid_username, Val} -> 109 | [<<"Il nome utente '">>, Val, 110 | <<"' non è valido. Sono ammessi solo caratteri, numeri e sottolineato ('_')">>]; 111 | invalid_login -> 112 | <<"Nome utente o password non validi">>; 113 | {too_short, Field, Min} -> 114 | [<<"Il campo ">>, Field, <<" è troppo corto (">>, Min, 115 | <<" caratteri almeno)">>]; 116 | password_mismatch -> 117 | <<"Le password non corrispondono">>; 118 | twitter_unauthorized -> 119 | <<"Il nome utente/password inseriti non sono stati accettati " 120 | "da Twitter!">>; 121 | twitter_authorization_error -> 122 | <<"Non sono riuscito a contattare Twitter. Per favore " 123 | " riprova più tardi.">>; 124 | {invalid_url, Field} -> 125 | [<<"Il campo URL ">>, Field, <<" deve iniziare con 'http://'">>]; 126 | 127 | %% confirmation messages 128 | settings_updated -> 129 | [<<"Le nuove impostazioni sono state salvate">>]; 130 | 131 | %% miscellaneous 132 | {seconds_ago, Val} -> [Val, <<" secondi fa">>]; 133 | {minutes_ago, Val} -> [Val, <<" minuti fa">>]; 134 | {hours_ago, Val} -> [Val, <<" ore fa">>]; 135 | {days_ago, Val} -> [Val, <<" giorni fa">>] 136 | end. 137 | -------------------------------------------------------------------------------- /src/bundles/twoorl_kor.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_kor). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | %% layout 22 | login -> <<"로그인">>; 23 | register -> <<"회원가입">>; 24 | logged_in_as -> <<"logged in as">>; 25 | settings -> <<"settings">>; 26 | logout -> <<"로그 오프">>; 27 | get_source -> 28 | <<"소스 잡으세요">>; 29 | 30 | %% navbar 31 | home -> <<"홈">>; 32 | replies -> <<"답장">>; 33 | me -> <<"나">>; 34 | everyone -> <<"사람들 모두">>; 35 | 36 | %% login page 37 | login_cap -> <<"로그인">>; 38 | username -> <<"아이디">>; 39 | password -> <<"비밀번호">>; 40 | not_member -> <<"회원 아니세요?">>; 41 | login_submit -> <<"로그인">>; 42 | 43 | %% register page 44 | register_cap -> <<"회원가입">>; 45 | email -> <<"이메일">>; 46 | password2 -> <<"비밀번호 다시 입력해보세요">>; 47 | already_member -> <<"벌써 회원 이세요?">>; 48 | 49 | %% home page 50 | upto -> <<"뭘 하세요?">>; 51 | twitter_msg -> <<"Automatic posting to Twitter enabled for " 52 | "non-replies">>; 53 | 54 | %% main page 55 | public_timeline -> <<"Public timeline">>; 56 | 57 | %% users page 58 | {no_user, Username} -> 59 | [Username,<<" 아이디를 못 찾았습니다. '">>]; 60 | {timeline_of, Username} -> 61 | [Username, <<"'s timeline">>]; 62 | following -> <<"following">>; 63 | followers -> <<"followers">>; 64 | follow -> <<"follow">>; 65 | unfollow -> <<"unfollow">>; 66 | 67 | %% friends page 68 | {friends_of, Userlink} -> 69 | [<<"People ">>, Userlink, <<" follows">>]; 70 | {followers_of, Userlink} -> 71 | [Userlink, <<"'s followers">>]; 72 | {no_friends, Username} -> 73 | [Username, <<" isn't following anyone">>]; 74 | {no_followers, Username} -> 75 | [Username, <<" has no followers">>]; 76 | 77 | 78 | %% settings page 79 | settings_cap -> <<"Settings">>; 80 | use_gravatar -> <<"Use Gravatar?">>; 82 | profile_bg -> <<"Profile background">>; 83 | profile_bg_help -> 84 | <<"Enter the url for your profile background image " 85 | "(leave blank to use the default background):">>; 86 | twitter_help -> 87 | <<"You may provide your Twitter account details to have " 88 | "your twoorls automatically posted to Twitter.

" 89 | "Only twoorls that don't contain replies (e.g." 90 | "\"@sergey\") will be posted to Twitter.">>; 91 | twitter_username -> <<"Twitter username:">>; 92 | twitter_password -> <<"Twitter password:">>; 93 | twitter_auto -> <<"Automatically post my Twoorls to Twitter?">>; 94 | submit -> <<"submit">>; 95 | 96 | %% error messages 97 | {missing_field, Field} -> 98 | [<<"The ">>, Field, <<" field is required">>]; 99 | {username_taken, Val} -> 100 | [<<"The username '">>, Val, <<"' is taken">>]; 101 | {invalid_username, Val} -> 102 | [<<"The username '">>, Val, 103 | <<"' is invalid. Only letters, numbers and underscore ('_') " 104 | "are accepted">>]; 105 | invalid_login -> 106 | <<"Invalid username or password">>; 107 | {too_short, Field, Min} -> 108 | [<<"The ">>, Field, <<" is too short (">>, Min, 109 | <<" chars minimum)">>]; 110 | password_mismatch -> 111 | <<"The password values didn't match">>; 112 | twitter_unauthorized -> 113 | <<"Twitter rejected the username/password combination you " 114 | "provided">>; 115 | twitter_authorization_error -> 116 | <<"Couldn't connect to Twitter. Please try again later.">>; 117 | {invalid_url, Field} -> 118 | [<<"The ">>, Field, <<" URL must start with 'http://'">>]; 119 | 120 | %% confirmation messages 121 | settings_updated -> 122 | [<<"Your settings have been updated successfully">>] 123 | end. 124 | -------------------------------------------------------------------------------- /src/bundles/twoorl_pol.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_pol). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | %% layout 22 | login -> <<"zaloguj">>; 23 | register -> <<"zajrejstruj">>; 24 | logged_in_as -> <<"zalogowany jako">>; 25 | settings -> <<"ustawienia">>; 26 | logout -> <<"wyloguj">>; 27 | get_source -> 28 | <<"Pobierz " 29 | "kod źródłowy">>; 30 | 31 | %% navbar 32 | home -> <<"początek">>; 33 | replies -> <<"odpowiedzi">>; 34 | me -> <<"ja">>; 35 | everyone -> <<"wszyscy">>; 36 | 37 | %% login page 38 | login_cap -> <<"Login">>; 39 | username -> <<"nazwa użytkownika">>; 40 | password -> <<"hasło">>; 41 | not_member -> <<"Nie jesteś członkiem?">>; 42 | login_submit -> <<"zaloguj">>; 43 | 44 | %% register page 45 | register_cap -> <<"Zarejstruj">>; 46 | username -> <<"nazwa użytkownika">>; 47 | email -> <<"adres poczty elektronicznej">>; 48 | password -> <<"hasło">>; 49 | password2 -> <<"powtórz hasło">>; 50 | already_member -> <<"Jesteś już członkiem?">>; 51 | %% login_cap -> <<"Zaloguj">>; 52 | 53 | %% home page 54 | upto -> <<"Co teraz robisz?">>; 55 | twitter_msg -> <<"Automatyczne wysyłanie do Twittera włączone dla " 56 | "'nie-odpowiedzi'">>; 57 | 58 | %% main page 59 | public_timeline -> <<"Publiczna historia">>; 60 | 61 | %% users page 62 | {no_user, Username} -> 63 | [<<"Użytkownik '">>, Username, <<"' nie istnieje">>]; 64 | {timeline_of, Username} -> 65 | [<<"Historia użytkownika ">>, Username]; 66 | following -> <<"śledzi">>; 67 | followers -> <<"śledzący">>; 68 | follow -> <<"śledź">>; 69 | unfollow -> <<"przestań śledzić">>; 70 | 71 | %% friends page 72 | {friends_of, Userlink} -> 73 | [<<"Użytkownicy których ">>, Userlink, <<" śledzi">>]; 74 | {followers_of, Userlink} -> 75 | [<<"Śledzący użytkownika ">>, Userlink]; 76 | {no_friends, Username} -> 77 | [Username, <<" nie śledzi nikogo">>]; 78 | {no_followers, Username} -> 79 | [<<"Nikt ie śledzi ">>, Username]; 80 | 81 | 82 | %% settings page 83 | settings_cap -> <<"Ustawienia">>; 84 | use_gravatar -> <<"Użyć Gravatara?">>; 86 | profile_bg -> <<"Tło profilu">>; 87 | profile_bg_help -> 88 | <<"Wprowadź URL dla obrazu tła Twojego profilu " 89 | "(zostaw puste dla domyślnego tła):">>; 90 | twitter_help -> 91 | <<"Możesz podać szczegóły Twojego konta Twitter aby automatycznie " 92 | "wysyłać również do Twittera.

" 93 | "Jedynie twoorle które nie zawierają odpowiedzi (np." 94 | "\"@tomek\") będą wysyłane do Twittera.">>; 95 | twitter_username -> <<"Nazwa użytkownika Twitter:">>; 96 | twitter_password -> <<"Hasło Twitter:">>; 97 | twitter_auto -> <<"Automatycznie wysyłaj moje Twoorle do Twittera?">>; 98 | submit -> <<"wyślij">>; 99 | 100 | %% error messages 101 | {missing_field, Field} -> 102 | [<<"Pole ">>, Field, <<" jest wymagane">>]; 103 | {username, Val} -> 104 | [<<"Nazwa użytkownika '">>, Val, <<"' jest zajęta">>]; 105 | {invalid_username, Val} -> 106 | [<<"Nazwa użytkownika '">>, Val, 107 | <<"' jest nieprawidłowa. Jedynie litery, cyfry oraz znak podkreślenia ('_') " 108 | "są dozwolone">>]; 109 | invalid_login -> 110 | <<"Niepoprawna nazwa użytkownika lub hasło">>; 111 | {too_short, Field, Min} -> 112 | [<<"Pole ">>, Field, <<" jest zbyt krótkie (minimum ">>, Min, 113 | <<" znaków)">>]; 114 | password_mismatch -> 115 | <<"Podane hasła nie są identyczne">>; 116 | twitter_unauthorized -> 117 | <<"Twitter odrzucił kombinację nazwy użytkownika oraz hasła " 118 | "które podałeś/podałaś">>; 119 | twitter_authorization_error -> 120 | <<"Nie mogę połączyć się z Twitter. Spróbuj ponownie później.">>; 121 | {invalid_url, Field} -> 122 | [<<"Pole ">>, Field, <<" z URL musi zaczynać się od 'http://'">>]; 123 | 124 | %% confirmation messages 125 | settings_updated -> 126 | [<<"Twoje ustawienia zostały poprawnie zaktualizowane">>] 127 | end. 128 | -------------------------------------------------------------------------------- /src/bundles/twoorl_por_br.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_por_br). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | %% layout 22 | login -> <<"conectar-se">>; 23 | register -> <<"registrar-se">>; 24 | logged_in_as -> <<"conectado como">>; 25 | settings -> <<"configurações">>; 26 | logout -> <<"sair">>; 27 | get_source -> 28 | <<"Obtenha o " 29 | "código fonte">>; 30 | 31 | %% navbar 32 | home -> <<"home">>; 33 | replies -> <<"respostas">>; 34 | me -> <<"eu">>; 35 | everyone -> <<"todo mundo">>; 36 | 37 | %% login page 38 | login_cap -> <<"Login">>; 39 | username -> <<"nome de usuário">>; 40 | password -> <<"senha">>; 41 | not_member -> <<"Ainda não cadastrado?">>; 42 | login_submit -> <<"login">>; 43 | 44 | %% register page 45 | register_cap -> <<"Criar sua conta">>; 46 | email -> <<"email">>; 47 | password2 -> <<"repita a senha">>; 48 | already_member -> <<"Já possui sua conta?">>; 49 | 50 | %% home page 51 | upto -> <<"O que você está fazendo agora?">>; 52 | twitter_msg -> <<"Postagem automática no Twitter habilitada para " 53 | "mensagens que não sejam respostas">>; 54 | 55 | %% main page 56 | public_timeline -> <<"Linha do tempo pública">>; 57 | 58 | %% users page 59 | {no_user, Username} -> 60 | [<<"O usuário '">>, Username, <<"' não existe">>]; 61 | {timeline_of, Username} -> 62 | [<<"Linha do tempo de ">>, Username]; 63 | following -> <<"está seguindo">>; 64 | followers -> <<"seus seguidores">>; 65 | follow -> <<"seguir">>; 66 | unfollow -> <<"deixar de seguir">>; 67 | 68 | %% friends page 69 | {friends_of, Userlink} -> 70 | [<<"Pessoas que ">>, Userlink, <<" segue">>]; 71 | {followers_of, Userlink} -> 72 | [<<"Seguidore de ">>, Userlink]; 73 | {no_friends, Username} -> 74 | [Username, <<" ainda não está seguindo ninguem">>]; 75 | {no_followers, Username} -> 76 | [Username, <<" não tem seguidores">>]; 77 | 78 | 79 | %% settings page 80 | settings_cap -> <<"Configurações">>; 81 | use_gravatar -> <<"Use Gravatar?">>; 83 | profile_bg -> <<"Imagem de fundo do seu perfil">>; 84 | profile_bg_help -> 85 | <<"Informe a url para a imagem de fundo do seu perfil " 86 | "(você pode deixar este campo em branco para usar o fundo default):">>; 87 | twitter_help -> 88 | <<"Você também pode informar os detalhes de sua conta no twitter " 89 | "para que seus twoorls sejam automaticamente postados no Twitter.

" 90 | "Apenas os twoorls que não tenham respostas (e.g." 91 | "\"@sergey\") serão postados no Twitter.">>; 92 | twitter_username -> <<"Nome de usuário no Twitter:">>; 93 | twitter_password -> <<"Senha do twitter:">>; 94 | twitter_auto -> <<"Postar meus Twoorls automaticamente no Twitter?">>; 95 | submit -> <<"submeter alterações">>; 96 | 97 | %% error messages 98 | {missing_field, Field} -> 99 | [<<"O ">>, Field, <<" campo é obrigatório">>]; 100 | {username_taken, Val} -> 101 | [<<"O nome de usuário '">>, Val, <<"' já está sendo utilizado">>]; 102 | {invalid_username, Val} -> 103 | [<<"O nome de usuário '">>, Val, 104 | <<"' é inválido. Apenas letras, números e underscores ('_') " 105 | "são permitidos">>]; 106 | invalid_login -> 107 | <<"Nome de usuário ou senha inválidos">>; 108 | {too_short, Field, Min} -> 109 | [<<"O campo ">>, Field, <<" é muito curto (">>, Min, 110 | <<" caracteres é o mínimo)">>]; 111 | password_mismatch -> 112 | <<"Os campos de senhas não são iguais.">>; 113 | twitter_unauthorized -> 114 | <<"O Twitter rejeitou a combinação de usuário e senha que você " 115 | "informou">>; 116 | twitter_authorization_error -> 117 | <<"Não foi possível conectar-se ao Twitter. Por favor tente novamente mais tarde.">>; 118 | {invalid_url, Field} -> 119 | [<<"O campo ">>, Field, <<" precisa começar com 'http://'">>]; 120 | 121 | %% confirmation messages 122 | settings_updated -> 123 | [<<"Suas configurações foram atualizadas com sucesso">>] 124 | end. 125 | -------------------------------------------------------------------------------- /src/bundles/twoorl_rus.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_rus). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | %% layout 22 | login -> <<"вход">>; 23 | register -> <<"регистрация">>; 24 | logged_in_as -> <<"ваше имя">>; 25 | settings -> <<"настройки">>; 26 | logout -> <<"выход">>; 27 | get_source -> 28 | <<"Получить " 29 | "исходный код">>; 30 | 31 | %% navbar 32 | home -> <<"домой">>; 33 | replies -> <<"ответ">>; 34 | me -> <<"я">>; 35 | everyone -> <<"все">>; 36 | 37 | %% login page 38 | login_cap -> <<"Вход">>; 39 | username -> <<"имя">>; 40 | password -> <<"пароль">>; 41 | not_member -> <<"Не зарегистрированы?">>; 42 | login_submit -> <<"Войти">>; 43 | 44 | %% register page 45 | register_cap -> <<"Регистрация">>; 46 | email -> <<"email">>; 47 | password2 -> <<"пароль (подтверждение)">>; 48 | already_member -> <<"Уже зарегистрировались?">>; 49 | 50 | %% home page 51 | upto -> <<"Чем занимаешься?">>; 52 | twitter_msg -> <<"Автоматическое копирование на Twitter работает для обычных сообщений," 53 | " не ответов">>; 54 | 55 | %% main page 56 | public_timeline -> <<"Общая лента">>; 57 | 58 | %% users page 59 | {no_user, Username} -> 60 | [<<"Пользователь '">>, Username, <<"' не существует">>]; 61 | {timeline_of, Username} -> 62 | [<<"Лента ">>, Username]; 63 | following -> <<"друзей">>; 64 | followers -> <<"наблюдающих">>; 65 | follow -> <<"следить">>; 66 | unfollow -> <<"не следить">>; 67 | 68 | %% friends page 69 | {friends_of, Userlink} -> 70 | [<<"Друзья ">>, Userlink]; 71 | {followers_of, Userlink} -> 72 | [<<"Наблюдатели ">>, Userlink]; 73 | {no_friends, Username} -> 74 | [Username, <<" не следит ни за кем">>]; 75 | {no_followers, Username} -> 76 | [<<"У ">>, Username, <<" нет наблюдателей">>]; 77 | 78 | 79 | %% settings page 80 | settings_cap -> <<"Настройки">>; 81 | use_gravatar -> <<"Использовать Gravatar?">>; 83 | profile_bg -> <<"Фоновое изображение">>; 84 | profile_bg_help -> 85 | <<"Введите адрес фонового изображения" 86 | "(оставьте пустым для изображения по-умолчанию):">>; 87 | twitter_help -> 88 | <<"Вы можете предоставить информацию о учетной записи в Twitter, чтобы" 89 | "ваши посты автоматически копировались в Twitter.

" 90 | "Только посты не содержащие ответы (например " 91 | "\"@edbond\") будут отправлены на Twitter.">>; 92 | twitter_username -> <<"Twitter имя:">>; 93 | twitter_password -> <<"Twitter пароль:">>; 94 | twitter_auto -> <<"Автоматически постить на Twitter?">>; 95 | submit -> <<"отправить">>; 96 | 97 | %% error messages 98 | {missing_field, Field} -> 99 | [Field, <<" обязательное поле">>]; 100 | {username_taken, Val} -> 101 | [<<"Имя '">>, Val, <<"' уже занято">>]; 102 | {invalid_username, Val} -> 103 | [<<"Имя '">>, Val, 104 | <<"' некорректно. Используйте только буквы, цифры и подчеркивание ('_') ">>]; 105 | invalid_login -> 106 | <<"Неправильное имя или пароль">>; 107 | {too_short, Field, Min} -> 108 | [<<"Поле ">>, Field, <<" очень короткое (">>, Min, 109 | <<" букв минимум)">>]; 110 | password_mismatch -> 111 | <<"Пароли не совпадают">>; 112 | twitter_unauthorized -> 113 | <<"Twitter отказал в доступе по имени и паролю, который вы указали">>; 114 | twitter_authorization_error -> 115 | <<"Не могу соединиться с Twitter. Пожалуйста, попробуйте позже.">>; 116 | {invalid_url, Field} -> 117 | [<<"Ссылка ">>, Field, <<" должна начинаться с 'http://'">>]; 118 | 119 | %% confirmation messages 120 | settings_updated -> 121 | [<<"Ваши настройки были успешно сохранены">>] 122 | end. 123 | -------------------------------------------------------------------------------- /src/bundles/twoorl_spa.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_spa). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | %% layout 22 | login -> <<"acceder">>; 23 | register -> <<"crear una cuenta">>; 24 | logged_in_as -> <<"conectado como">>; 25 | settings -> <<"configuración">>; 26 | logout -> <<"salir">>; 27 | get_source -> 28 | <<"Obtener el " 29 | "código fuente">>; 30 | 31 | %% navbar 32 | home -> <<"inicio">>; 33 | replies -> <<"respuestas">>; 34 | me -> <<"yo">>; 35 | everyone -> <<"todos">>; 36 | 37 | %% login page 38 | login_cap -> <<"Acceder">>; 39 | username -> <<"nombre de usuario">>; 40 | password -> <<"contraseña">>; 41 | not_member -> <<"No dispone de una cuenta?">>; 42 | login_submit -> <<"acceder">>; 43 | 44 | %% register page 45 | register_cap -> <<"Crear una cuenta">>; 46 | email -> <<"dirección de correo electrónico">>; 47 | password2 -> <<"vuelve a introducir la contraseña">>; 48 | already_member -> <<"Ya dispone de una cuenta?">>; 49 | 50 | %% home page 51 | upto -> <<"Qué estás haciendo?">>; 52 | twitter_msg -> <<"Envío automático a Twitter activado para " 53 | "mensajes sin respuestas">>; 54 | 55 | %% main page 56 | public_timeline -> <<"Cronograma público">>; 57 | 58 | %% users page 59 | {no_user, Username} -> 60 | [<<"El usuario '">>, Username, <<"' no existe">>]; 61 | {timeline_of, Username} -> 62 | [<<"Cronograma de">>, Username]; 63 | following -> <<"siguiendo">>; 64 | followers -> <<"seguidores">>; 65 | follow -> <<"seguir">>; 66 | unfollow -> <<"no seguir más">>; 67 | 68 | %% friends page 69 | {friends_of, Userlink} -> 70 | [<<"Gente que ">>, Userlink, <<" sigue">>]; 71 | {followers_of, Userlink} -> 72 | [<<"Seguidores de">>, Userlink]; 73 | {no_friends, Username} -> 74 | [Username, <<" no sigue a nadie">>]; 75 | {no_followers, Username} -> 76 | [Username, <<" no es seguido por nadie">>]; 77 | 78 | 79 | %% settings page 80 | settings_cap -> <<"Configuración">>; %% jdilelle says: can also be "Parámetros". 81 | use_gravatar -> <<"Usar un Gravatar?">>; 83 | profile_bg -> <<"Fondo de pantalla de tu perfil">>; 84 | profile_bg_help -> 85 | <<"Proporcione el URL de la imágen de fondo de pantalla " 86 | "(dejar vacío para usar el fondo de pantalla por defecto):">>; 87 | twitter_help -> 88 | <<"Puedes proporcionar los detalles de tu cuenta Twitter para que tus " 89 | "twoorls sean enviados automáticamente a Twitter.

" 90 | "Sólo los twoorls sin respuestas (por ejemplo" 91 | "\"@sergey\") serán enviados a Twitter.">>; 92 | twitter_username -> <<"Nombre de usuario Twitter:">>; 93 | twitter_password -> <<"Contraseña Twitter:">>; 94 | twitter_auto -> <<"Enviar automáticamente mis Twoorls a Twitter?">>; 95 | submit -> <<"enviar">>; 96 | 97 | %% error messages 98 | {missing_field, Field} -> 99 | [<<"El campo ">>, Field, <<" es requerido">>]; 100 | {username, Val} -> 101 | [<<"En nombre de usuario '">>, Val, <<"' no está disponible">>]; 102 | {invalid_username, Val} -> 103 | [<<"El nombre de usuatio '">>, Val, 104 | <<"' no es válido. Se aceptan solo letras, números y rayita de subrayado ('_') ">>]; 105 | invalid_login -> 106 | <<"Nombre de usuario o contraseña inválidas">>; 107 | {too_short, Field, Min} -> 108 | [<<"El campo ">>, Field, <<" es demasiado corto (">>, Min, 109 | <<" chars minimum)">>]; 110 | password_mismatch -> 111 | <<"Las contraseñas no coinciden">>; 112 | twitter_unauthorized -> 113 | <<"Twitter no ha aceptado el nombre de usuario y contraseña que " 114 | "has proporcionado">>; 115 | twitter_authorization_error -> 116 | <<"Fallo conectandose a Twitter. Por favor vuelva a intentar más tarde.">>; 117 | {invalid_url, Field} -> 118 | [<<"El campo URL ">>, Field, <<" debe comenzar con 'http://'">>]; 119 | 120 | %% confirmation messages 121 | settings_updated -> 122 | [<<"Su configuración ha sido aplicada">>] 123 | end. 124 | -------------------------------------------------------------------------------- /src/bundles/twoorl_sv.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_sv). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | 22 | language -> <<"svenska">>; 23 | 24 | %% layout 25 | login -> <<"logga in">>; 26 | register -> <<"registrera">>; 27 | logged_in_as -> <<"inloggad som">>; 28 | settings -> <<"inställningar">>; 29 | logout -> <<"logga ut">>; 30 | get_source -> 31 | <<"Hämta " 32 | "källkoden">>; 33 | 34 | %% navbar 35 | home -> <<"hem">>; 36 | replies -> <<"svar">>; 37 | me -> <<"jag">>; 38 | everyone -> <<"alla">>; 39 | 40 | %% login page 41 | login_cap -> <<"Logga in">>; 42 | username -> <<"användarnamn">>; 43 | password -> <<"lösenord">>; 44 | not_member -> <<"Inte medlem?">>; 45 | login_submit -> <<"logga in">>; 46 | 47 | %% register page 48 | % note: 'username', 'password' and 'Login_cap' are taken from 49 | % login page section 50 | register_cap -> <<"Registrera">>; 51 | email -> <<"e-post">>; 52 | password2 -> <<"upprepa lösenord">>; 53 | already_member -> <<"Redan medlem?">>; 54 | 55 | %% home page 56 | upto -> <<"Vad pysslar du med?">>; 57 | twitter_msg -> <<"Automatisk postning till Twitter aktiverat för " 58 | "icke-svar">>; 59 | send -> <<"skicka">>; 60 | 61 | %% main page 62 | public_timeline -> <<"Publik tidslinje">>; 63 | 64 | %% users page 65 | {no_user, Username} -> 66 | [<<"Användaren '">>, Username, <<"' finns inte">>]; 67 | {timeline_of, Username} -> 68 | [Username, <<" tidslinje">>]; 69 | following -> <<"följer">>; 70 | followers -> <<"förföljare">>; 71 | follow -> <<"följ">>; 72 | unfollow -> <<"ofölj">>; 73 | 74 | %% friends page 75 | {friends_of, Userlink} -> 76 | [<<"Människor ">>, Userlink, <<" följer">>]; 77 | {followers_of, Userlink} -> 78 | [Userlink, <<" förföljare">>]; 79 | {no_friends, Username} -> 80 | [Username, <<" följer ingen">>]; 81 | {no_followers, Username} -> 82 | [Username, <<" har inga förföljare">>]; 83 | 84 | 85 | %% settings page 86 | settings_cap -> <<"Inställningar">>; 87 | use_gravatar -> <<"Använd Gravatar?">>; 89 | profile_bg -> <<"Profilbakgrund">>; 90 | profile_bg_help -> 91 | <<"Skriv in url.en för din profilbakgrundsbild " 92 | "(lämna blankt för att använda standardbakgrunden):">>; 93 | twitter_help -> 94 | <<"Du kan tillhandahålla dina Twitterkontouppgifter för att få" 95 | "dina twoorls automatiskt postade till Twitter.

" 96 | "Bara twoorls som inte innehåller svar (ex." 97 | "\"@sergey\") kommer att postas till Twitter">>; 98 | twitter_username -> <<"Twitter användarnamn:">>; 99 | twitter_password -> <<"Twitter lösenord:">>; 100 | twitter_auto -> <<"Posta automatiskt mina Twoorls till Twitter?">>; 101 | submit -> <<"skicka">>; 102 | 103 | %% error messages 104 | {missing_field, Field} -> 105 | [<<"">>, Field, <<" fältet är obligatoriskt">>]; 106 | {username_taken, Val} -> 107 | [<<"Användarnamnet '">>, Val, <<"' är taget">>]; 108 | {invalid_username, Val} -> 109 | [<<"Användarnamnet '">>, Val, 110 | <<"' är ogiltigt. Enbart bokstäver, nummer och understreck ('_') " 111 | " är tillåtna">>]; 112 | invalid_login -> 113 | <<"Ogiltigt användarnamn eller lösenord">>; 114 | {too_short, Field, Min} -> 115 | [<<"">>, Field, <<" är för kort (">>, Min, 116 | <<" tecken minst)">>]; 117 | password_mismatch -> 118 | <<"Lösenorden matchade inte">>; 119 | twitter_unauthorized -> 120 | <<"Twitter ratade användarnamn/lösenords kombinationen du " 121 | "tillhandahöll">>; 122 | twitter_authorization_error -> 123 | <<"Kund einte koppla upp mot Twitter. Var vänlig försök igen senare.">>; 124 | {invalid_url, Field} -> 125 | [<<"">>, Field, <<" URL:en måste börja med 'http://'">>]; 126 | 127 | %% confirmation messages 128 | settings_updated -> 129 | [<<"Dina inställningar har uppdaterats framgångsrikt">>]; 130 | 131 | %% miscellaneous 132 | {seconds_ago, Val} -> [Val, <<" sekunder sedan">>]; 133 | {minutes_ago, Val} -> [Val, <<" minuter sedan">>]; 134 | {hours_ago, Val} -> [Val, <<" timmar sedan">>]; 135 | {days_ago, Val} -> [Val, <<" dagar sedan">>] 136 | end. 137 | -------------------------------------------------------------------------------- /src/bundles/twoorl_zh_cn.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | 16 | -module(twoorl_zh_cn). 17 | -export([bundle/1]). 18 | 19 | bundle(Tag) -> 20 | case Tag of 21 | 22 | language -> <<"简体中文">>; 23 | 24 | %% layout 25 | login -> <<"登录">>; 26 | register -> <<"注册">>; 27 | logged_in_as -> <<"登录为">>; 28 | settings -> <<"设置">>; 29 | logout -> <<"登出">>; 30 | get_source -> 31 | <<"获取 " 32 | "源代码">>; 33 | 34 | %% navbar 35 | home -> <<"首页">>; 36 | replies -> <<"回复">>; 37 | me -> <<"我">>; 38 | everyone -> <<"所有人">>; 39 | 40 | %% login page 41 | login_cap -> <<"登录">>; 42 | username -> <<"用户名">>; 43 | password -> <<"密码">>; 44 | not_member -> <<"不是注册用户?">>; 45 | login_submit -> <<"登录">>; 46 | 47 | %% register page 48 | % note: 'username', 'password' and 'Login_cap' are taken from 49 | % login page section 50 | register_cap -> <<"注册">>; 51 | email -> <<"邮箱">>; 52 | password2 -> <<"再次输入密码">>; 53 | already_member -> <<"已经注册?">>; 54 | 55 | %% home page 56 | upto -> <<"我在做什么?">>; 57 | twitter_msg -> <<"自动发表到twitter并启用非回复">>; 58 | send -> <<"发表">>; 59 | 60 | %% main page 61 | public_timeline -> <<"公共时间表">>; 62 | 63 | %% users page 64 | {no_user, Username} -> 65 | [<<"用户名 '">>, Username, <<"' 不存在">>]; 66 | {timeline_of, Username} -> 67 | [Username, <<"的时间表">>]; 68 | following -> <<"我关注的人">>; 69 | followers -> <<"关注我的人">>; 70 | follow -> <<"关注">>; 71 | unfollow -> <<"取消关注">>; 72 | 73 | %% friends page 74 | {friends_of, Userlink} -> 75 | [<<"用户 ">>, Userlink, <<" 关注的人">>]; 76 | {followers_of, Userlink} -> 77 | [<<"关注 ">>, Userlink, <<" 的人">>]; 78 | {no_friends, Username} -> 79 | [Username, <<" 没有关注任何人">>]; 80 | {no_followers, Username} -> 81 | [Username, <<" 没有被任何人关注">>]; 82 | 83 | 84 | %% settings page 85 | settings_cap -> <<"配置">>; 86 | use_gravatar -> <<"使用 Gravatar?">>; 88 | profile_bg -> <<"介绍的背景图">>; 89 | profile_bg_help -> 90 | <<"请输入您的介绍页面背景图的网址 " 91 | "(留空时使用默认背景图):">>; 92 | twitter_help -> 93 | <<"您可以提供您的twitter帐户的详细资料," 94 | "让您的消息自动发表到twitter。

" 95 | "只有不为回复的消息 (例如 " 96 | "\"@sergey\") 才会发表到 Twitter.">>; 97 | twitter_username -> <<"Twitter 用户名:">>; 98 | twitter_password -> <<"Twitter 密码:">>; 99 | twitter_auto -> <<"自动发表我的消息到Twitter?">>; 100 | submit -> <<"提交">>; 101 | 102 | %% error messages 103 | {missing_field, Field} -> 104 | [<<"请输入 ">>, Field, <<" 的内容">>]; 105 | {username_taken, Val} -> 106 | [<<"帐号 '">>, Val, <<"' 已经被使用">>]; 107 | {invalid_username, Val} -> 108 | [<<"帐号 '">>, Val, 109 | <<"' 不合法。只能输入英文字母、数字和下划线 ('_') ">>]; 110 | invalid_login -> 111 | <<"错误的用户名或密码">>; 112 | {too_short, Field, Min} -> 113 | [<<"输入框 ">>, Field, <<" 内容太短 (至少 ">>, Min, 114 | <<" 个英文字母)">>]; 115 | password_mismatch -> 116 | <<"两次输入的密码不匹配">>; 117 | twitter_unauthorized -> 118 | <<"您输入的Twitter帐号和密码组合错误">>; 119 | twitter_authorization_error -> 120 | <<"连接不上Twitter,请重试">>; 121 | {invalid_url, Field} -> 122 | [<<"网址 ">>, Field, <<" 必须以 'http://' 开头">>]; 123 | 124 | %% confirmation messages 125 | settings_updated -> 126 | [<<"您的设置已经成功更新了">>]; 127 | 128 | %% miscellaneous 129 | {seconds_ago, Val} -> [Val, <<" 秒之前">>]; 130 | {minutes_ago, Val} -> [Val, <<" 分钟之前">>]; 131 | {hours_ago, Val} -> [Val, <<" 小时之前">>]; 132 | {days_ago, Val} -> [Val, <<" 天之前">>] 133 | end. 134 | -------------------------------------------------------------------------------- /src/components/api_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(api_controller). 22 | -export([send/1, follow/1, toggle_twitter/1]). 23 | -include("twoorl.hrl"). 24 | 25 | send(A) -> 26 | twoorl_util:auth( 27 | A, 28 | fun(Usr) -> 29 | Params = yaws_api:parse_post(A), 30 | {[Body], Errs} = 31 | erlyweb_forms:validate( 32 | Params, ["msg"], 33 | fun("msg", Val) -> 34 | case Val of 35 | [] -> 36 | {error, empty_msg}; 37 | _ -> 38 | %% helps avoid DOS 39 | {ok, lists:sublist(Val, ?MAX_MSG_SIZE)} 40 | end 41 | end), 42 | case Errs of 43 | [] -> 44 | {Body1, BodyNoLinks, RecipientNames} = 45 | msg:process_raw_body(Body), 46 | 47 | TwitterEnabled = Usr:twitter_enabled() == 1, 48 | TwitterStatus = 49 | if TwitterEnabled -> 50 | ?TWITTER_SENT_PENDING; 51 | true -> 52 | ?TWITTER_NOT_SENT 53 | end, 54 | Msg = msg:new_with([{usr_username, Usr:username()}, 55 | {usr_id, Usr:id()}, 56 | {body, Body1}, 57 | {body_nolinks, BodyNoLinks}, 58 | {body_raw, Body}, 59 | {usr_gravatar_id, 60 | twoorl_util:gravatar_id( 61 | Usr:email())}, 62 | {usr_gravatar_enabled, 63 | Usr:gravatar_enabled()}, 64 | {twitter_status, TwitterStatus}, 65 | {spam, Usr:spammer()}]), 66 | Msg1 = Msg:save(), 67 | 68 | %% twoorl_stats:cast({record, twoorl}), 69 | 70 | if TwitterEnabled andalso RecipientNames == [] -> 71 | spawn(twoorl_twitter, send_tweet, [Usr, Msg1]); 72 | true -> 73 | ok 74 | end, 75 | 76 | spawn( 77 | fun() -> 78 | RecipientIds = 79 | usr:find( 80 | {username, in, 81 | lists:usort( 82 | [Name || Name <- RecipientNames])}), 83 | reply:save_replies(Msg1:id(), RecipientIds) 84 | end), 85 | 86 | case proplists:get_value("get_html", Params) of 87 | "true" -> 88 | Msg2 = msg:created_on( 89 | Msg1, calendar:local_time()), 90 | {ewc, timeline, show_msg, [A, Msg2]}; 91 | _ -> 92 | {data, "ok"} 93 | end; 94 | _ -> 95 | 96 | %% TODO need decent error reporting 97 | exit(Errs) 98 | end 99 | end). 100 | 101 | follow(A) -> 102 | twoorl_util:auth( 103 | A, 104 | fun(Usr) -> 105 | {[{Username, OtherUsrId}, Val], Errs} = 106 | erlyweb_forms:validate( 107 | A, ["username", "value"], 108 | fun(F, V) -> 109 | if V == [] -> 110 | {error, {missing_field, F}}; 111 | F == "username" -> 112 | OtherUsr = usr:find_first( 113 | {username,'=',V}), 114 | if OtherUsr == undefined -> 115 | {error, {invalid_value, {F, V}}}; 116 | true -> 117 | {ok, {V, OtherUsr:id()}} 118 | end; 119 | F == "value" -> 120 | if V == "0" orelse V == "1" -> 121 | {ok, V == "1"}; 122 | true -> 123 | {error, {invalid_value, {F, V}}} 124 | end 125 | end 126 | end), 127 | Errs2 = 128 | if Errs == [] -> 129 | if Val -> 130 | following:insert( 131 | following:new_with( 132 | [{usr_id1,Usr:id()}, 133 | {usr_id2,OtherUsrId}, 134 | {usr_username1,Usr:username()}, 135 | {usr_username2,Username}])), 136 | []; 137 | true -> 138 | case following:delete_where( 139 | {'and', 140 | [ 141 | {usr_id1,'=',Usr:id()}, 142 | {usr_id2,'=',OtherUsrId}]}) of 143 | 0 -> 144 | [{error, {not_following, Username}}]; 145 | 1 -> 146 | [] 147 | end 148 | end; 149 | true -> 150 | Errs 151 | end, 152 | 153 | %% TODO need decent error reporting 154 | if Errs2 == [] -> 155 | {data, "ok"}; 156 | true -> 157 | exit(Errs2) 158 | end 159 | end). 160 | 161 | toggle_twitter(A) -> 162 | twoorl_util:auth( 163 | A, 164 | fun(Usr) -> 165 | Params = yaws_api:parse_post(A), 166 | Enabled = 167 | case proplists:get_value("value", Params) of 168 | "true" -> 1; 169 | "false" -> 0; 170 | %% TODO need better error handling 171 | Val -> exit({unexpected_value, Val}) 172 | end, 173 | Usr1 = usr:twitter_enabled(Usr, Enabled), 174 | twoorl_util:update_session(A, Usr1), 175 | usr:update([{twitter_enabled, Enabled}], {id,'=',Usr:id()}), 176 | {response, [{html, <<"ok">>}]} 177 | end). 178 | 179 | -------------------------------------------------------------------------------- /src/components/api_view.et: -------------------------------------------------------------------------------- 1 | <%@ send(Data) %><% Data %> 2 | <%@ follow(Data) %><% Data %> 3 | <%@ toggle_twitter(Data) %><% Data %> 4 | -------------------------------------------------------------------------------- /src/components/feeds_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Nick Gerakines, 2008 17 | %% 18 | %% @author Nick Gerakines [http://blog.socklabs.com/] 19 | %% @copyright Nick Gerakines, 2008 20 | %% 21 | %% @author Yariv Sadan [http://yarivsblog@gmail.com] 22 | %% @copyright Yariv Sadan, 2008 23 | -module(feeds_controller). 24 | -export([catch_all/2]). 25 | 26 | catch_all(A, ["main", Type]) -> 27 | Messages = msg:find_with([{order_by, {created_on, desc}}, {limit, 20}]), 28 | return(Type, {Type, 29 | <<"Twoorl / Everyone">>, 30 | <<"http://twoorl.com/main">>, 31 | <<"Latest twoorls from everyone">>, 32 | get_funs(A, Messages)}); 33 | 34 | catch_all(A, ["users", Username, Type]) -> 35 | case usr:find_first({username,'=',Username}) of 36 | undefined -> 37 | exit({no_such_user, Username}); 38 | Usr -> 39 | Messages = msg:find( 40 | {usr_id,in, [Usr:id()]}, 41 | [{order_by, {created_on, desc}}, {limit, 20}]), 42 | return(Type, 43 | {Type, 44 | [<<"Twoorl / ">>, Username], 45 | [<<"http://twoorl.com/users/">>, Username], 46 | [Username, <<"'s latest twoorls">>], 47 | get_funs(A, Messages)}) 48 | end; 49 | 50 | catch_all(A, ["friends", Username, Type]) -> 51 | case usr:find_first({username, '=', Username}) of 52 | undefined -> 53 | exit({no_such_user, Username}); 54 | Usr -> 55 | Ids = usr:get_timeline_usr_ids(Usr), 56 | Messages = msg:find( 57 | {usr_id,in, [Ids]}, 58 | [{order_by, {created_on, desc}}, {limit, 20}] 59 | ), 60 | return(Type, 61 | {Type, 62 | [<<"Twoorl / ">>, Username], 63 | [<<"http://twoorl.com/users/">>, Username], 64 | [Username, <<"'s friend's latest twoorls">>], 65 | get_funs(A, Messages)}) 66 | end; 67 | 68 | catch_all(A, _) -> 69 | catch_all(A, ["main", "rss"]). 70 | 71 | get_funs(A, Messages) -> 72 | [fun(title) -> 73 | [M:usr_username(), $:, 32, 74 | M:body_nolinks()]; 75 | (description) -> 76 | [M:usr_username(), $:, 32, 77 | M:body_nolinks()]; 78 | (htmldescription) -> 79 | [M:usr_username(), $:, 32, 80 | M:body_nolinks()]; 81 | (pubdate) -> 82 | twoorl_util:format_datetime( 83 | element(2,M:created_on())); 84 | (guid) -> 85 | msg:get_href(A, M, absolute); 86 | (link) -> 87 | msg:get_href(A, M, absolute) 88 | end || M <- Messages]. 89 | 90 | return(Format, Val) -> 91 | ContentType = 92 | case Format of 93 | "json" -> "application/json"; 94 | "atom" -> "application/atom+xml"; 95 | "rss" -> "application/xml" 96 | end, 97 | {response, 98 | [{body, {data, Val}}, 99 | {header, {content_type, ContentType}}]}. 100 | -------------------------------------------------------------------------------- /src/components/feeds_view.et: -------------------------------------------------------------------------------- 1 | <%@ catch_all({"rss", Title, Link, Description, Items}) %> 2 | 3 | 4 | 5 | <% Title %> 6 | <% Link %> 7 | <% Description %> 8 | en-us 9 | 40 10 | <% rss_items(Items) %> 11 | 12 | 13 | 14 | <%@ catch_all({"atom", Title, Link, Description, Items}) %> 15 | 16 | 17 | <% Title %> 18 | tag:twoorl.com:statuses:user 19 | 20 | <% Description %> 21 | <% atom_items(Items) %> 22 | 23 | 24 | <%@ catch_all({"json", Title, Link, Description, Items}) %> 25 | {"title": "<% Title %>", 26 | "link": "<% Link %>", 27 | "statuses":[ 28 | <% json_items(Items, 0) %> 29 | ] 30 | } 31 | <%@ rss_items([]) %> 32 | <%@ rss_items([Item | Rest]) %> 33 | 34 | <% Item(title) %> 35 | <% Item(description) %> 36 | <% Item(pubdate) %> 37 | <% Item(guid) %> 38 | <% Item(link) %> 39 | 40 | <% rss_items(Rest) %> 41 | 42 | <%@ atom_items([]) %> 43 | <%@ atom_items([Item | Rest]) %> 44 | 45 | <% Item(title) %> 46 | <% Item(htmldescription) %> 47 | tag:twoorl.com,<% Item(pubdate) %>:<% Item(link) %> 48 | <% Item(pubdate) %> 49 | <% Item(pubdate) %> 50 | 51 | 52 | <% atom_items(Rest) %> 53 | 54 | <%@ json_items([], _) %> 55 | <%@ json_items([Item | Rest], 0) %> 56 | { "date": "<% Item(pubdate) %>", "message": "<% Item(title) %>", "link": "<% Item(link) %>"} <% json_items(Rest, 1) %> 57 | <%@ json_items([Item | Rest], _) %> 58 | , { "date": "<% Item(pubdate) %>", "message": "<% Item(title) %>", "link": "<% Item(link) %>"} <% json_items(Rest, 1) %> 59 | -------------------------------------------------------------------------------- /src/components/followers_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(followers_controller). 22 | -compile(export_all). 23 | 24 | catch_all(A, [Username]) -> 25 | {replace, {ewc, user_list, show_related, [A, Username, false]}}. 26 | -------------------------------------------------------------------------------- /src/components/following.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(following). 22 | -compile(export_all). 23 | 24 | -------------------------------------------------------------------------------- /src/components/following_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(following_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | catch_all(A, [Username]) -> 26 | {replace, {ewc, user_list, show_related, [A, Username, true]}}. 27 | -------------------------------------------------------------------------------- /src/components/following_view.et: -------------------------------------------------------------------------------- 1 | <%@ catch_all([Username, Data]) %> 2 |
People <% Username %> follows
3 | <% Data %> 4 | -------------------------------------------------------------------------------- /src/components/grid_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | -module(grid_controller). 21 | -export([private/0, index/2, index/3]). 22 | 23 | private() -> 24 | true. 25 | 26 | index(A, Cells) -> 27 | index(A, Cells, 3). 28 | 29 | index(_A, Cells, NumCols) -> 30 | {_, Rows1} = 31 | lists:foldl( 32 | fun(Cell, {N, [Hd | Tl]}) when N == NumCols -> 33 | {1, [[Cell] | 34 | [lists:reverse(Hd) | Tl]]}; 35 | (Cell, {N, [Hd | Tl]}) -> 36 | {N+1, [[Cell | Hd] | Tl]} 37 | end, {0, [[]]}, Cells), 38 | lists:reverse(Rows1). 39 | -------------------------------------------------------------------------------- /src/components/grid_view.et: -------------------------------------------------------------------------------- 1 | <%@ index(Rows) %> 2 | 3 | <% [row(Row) || Row <- Rows] %> 4 |
5 | 6 | <%@ row(Row) %> 7 | 8 | <% cells(Row) %> 9 | 10 | 11 | <%@ cells([]) %> 12 | <%@ cells([Hd]) %> 13 | <% Hd %> 14 | <%@ cells([Hd | Tl]) %> 15 | <% Hd %><% cells(Tl) %> 16 | -------------------------------------------------------------------------------- /src/components/home_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(home_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | index(A) -> 26 | twoorl_util:auth( 27 | A, 28 | fun(Usr) -> 29 | Ids = usr:get_timeline_usr_ids(Usr), 30 | HasTwitter = ( 31 | Usr:twitter_username() =/= undefined andalso 32 | Usr:twitter_password() =/= undefined), 33 | [?Data(A, {Usr:username(), HasTwitter, 34 | Usr:twitter_enabled() == 1}), 35 | {ewc, timeline, show, [A, Ids]}] 36 | end). 37 | -------------------------------------------------------------------------------- /src/components/home_view.et: -------------------------------------------------------------------------------- 1 | <%@ index([{B, {Username, HasTwitter, TwitterEnabled}}, Timeline]) %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 17 | 18 |
<% B(upto) %>
140
9 |
10 |
11 | 12 | <% if HasTwitter -> twitter_msg(B, TwitterEnabled); true ->[] end %> 13 |
14 |
15 | 16 |
19 |
20 | 21 | <% Timeline %> 22 | 23 | <%@ twitter_msg(B, TwitterEnabled) %> 24 |
<<"checked=\"checked\"">>; 25 | true -> [] end %>/><% B(twitter_msg) %>
26 | 27 | -------------------------------------------------------------------------------- /src/components/html_container_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(html_container_controller). 22 | -export([private/0, index/3]). 23 | -include("twoorl.hrl"). 24 | 25 | private() -> 26 | true. 27 | 28 | index(_A, Ewc, PhasedVars) -> 29 | Background = case proplists:get_value(background, PhasedVars) of 30 | undefined -> 31 | ?DEFAULT_BACKGROUND; 32 | Bg -> 33 | Bg 34 | end, 35 | HeaderItems = case proplists:get_value(header_items, PhasedVars) of 36 | undefined -> []; 37 | Other -> Other 38 | end, 39 | [{data, {Background, HeaderItems}}, Ewc]. 40 | -------------------------------------------------------------------------------- /src/components/html_container_view.et: -------------------------------------------------------------------------------- 1 | <%@ index([{Background, HeaderItems}, Data]) %> 2 | 4 | 5 | 6 | 7 | Twoorl 8 | 9 | 10 | 15 | <% [header_item(I) || I <- HeaderItems] %> 16 | 17 | 18 | 19 |
20 | <% Data %> 21 |
22 | 23 | 24 | 25 | <% twoorl_snippets:google_analytics() %> 26 | 27 | 28 | 29 | <%@ header_item({feed_link, Format, Title, Href}) %> 30 | -------------------------------------------------------------------------------- /src/components/language_select_controller.erl: -------------------------------------------------------------------------------- 1 | -module(language_select_controller). 2 | -compile(export_all). 3 | -include("twoorl.hrl"). 4 | 5 | private() -> 6 | true. 7 | 8 | index(A) -> 9 | Lang = case erlyweb_util:get_cookie("lang", A) of 10 | undefined -> <<"eng">>; 11 | Other -> list_to_binary(Other) 12 | end, 13 | {data, Lang}. 14 | -------------------------------------------------------------------------------- /src/components/language_select_view.et: -------------------------------------------------------------------------------- 1 | <%@ index(SelectedLanguage) %> 2 |
3 | 7 |
8 | 9 | <%@ option({Val, Opt, _Bundle}, Selected) %> 10 | 11 | -------------------------------------------------------------------------------- /src/components/layout_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(layout_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | private() -> 26 | true. 27 | 28 | index(A, Ewc) -> 29 | Usr = twoorl_util:get_usr(A), 30 | {Username, Navbar} = 31 | if Usr =/= undefined -> 32 | {Usr:username(), {ewc, navbar, [A]}}; 33 | true -> 34 | {undefined, {data, []}} 35 | end, 36 | [?Data(A, Username), Navbar, Ewc, {ewc, language_select, [A]}]. 37 | -------------------------------------------------------------------------------- /src/components/layout_view.et: -------------------------------------------------------------------------------- 1 | <%@ index([{B, Username}, Navbar, Ewc, LanguageSelect]) %> 2 | 3 | 4 | 5 | 6 | 7 |
8 | <% Navbar %> 9 | <% Ewc %> 10 | <% LanguageSelect %> 11 | 12 | 13 | <%@ header(B, undefined) %><% B(login) %> | <% B(register) %> 14 | <%@ header(B, Username) %><% B(logged_in_as) %> <% Username %> | 15 | <% B(settings) %> | 16 | <% B(logout) %> 17 | -------------------------------------------------------------------------------- /src/components/login_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(login_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | index(A) -> 26 | case yaws_arg:method(A) of 27 | 'POST' -> 28 | {[Usr, Password], Errs} = 29 | erlyweb_forms:validate( 30 | A, 31 | ["username", "password"], 32 | fun(Field, Val) -> 33 | case Val of 34 | [] -> {error, {missing_field, Field}}; 35 | _ -> 36 | case Field of 37 | "username" -> 38 | get_usr(Val); 39 | _ -> 40 | ok 41 | end 42 | end 43 | end), 44 | Errs1 = 45 | if Errs == [] -> 46 | %% twoorl_stats:cast({record, site_login}), 47 | Hash = crypto:sha([usr:username(Usr), Password]), 48 | case usr:password(Usr) of 49 | Hash -> 50 | []; 51 | _ -> 52 | [invalid_login] 53 | end; 54 | true -> 55 | Errs 56 | end, 57 | if Errs1 == [] -> 58 | do_login(A, Usr); 59 | true -> 60 | [?Data(A, undefined), 61 | {ewc, ui_msgs, [A, Errs1]}] 62 | end; 63 | _ -> 64 | [?Data(A, undefined), {data, []}] 65 | end. 66 | 67 | get_usr(Username) -> 68 | Usr = usr:find_first({username,'=',Username}), 69 | if Usr == undefined -> 70 | {error, invalid_login}; 71 | true -> 72 | {ok, Usr} 73 | end. 74 | 75 | do_login(A, Usr) -> 76 | Key = twoorl_util:gen_key(), 77 | LangCookie = erlyweb_util:get_cookie("lang", A), 78 | Usr1 = case LangCookie of 79 | undefined -> 80 | Usr; 81 | Lang -> 82 | LangBin = list_to_binary(Lang), 83 | case Usr:language() of 84 | LangBin -> 85 | Usr; 86 | _ -> 87 | spawn(fun() -> 88 | usr:update([{language, Lang}], 89 | {id,'=',Usr:id()}) 90 | end), 91 | usr:language(Usr, Lang) 92 | end 93 | end, 94 | 95 | twoorl_util:update_session(A, Usr1, Key), 96 | spawn(fun() -> 97 | usr:update([{session_key, Key}], {id,'=',usr:id(Usr1)}) 98 | end), 99 | Response = [twoorl_util:cookie("key", Key)], 100 | 101 | %% set the language cookie for the session if it's not defined 102 | Response1 = if LangCookie == undefined -> 103 | case Usr1:language() of 104 | undefined -> 105 | Response; 106 | Other -> 107 | [twoorl_util:cookie("lang", Other) | Response] 108 | end; 109 | true -> 110 | Response 111 | end, 112 | {response, [{ewr, home} | Response1]}. 113 | 114 | -------------------------------------------------------------------------------- /src/components/login_view.et: -------------------------------------------------------------------------------- 1 | <%@ index([{B, _}, UiMsgs]) %> 2 |
<% B(login_cap) %>
3 | <% UiMsgs %> 4 |
5 |
<% B(username) %>:
6 |
7 |
<% B(password) %>:
8 |
9 |
10 |
11 | <% B(not_member) %> <% B(register_cap) %> 12 | 13 | -------------------------------------------------------------------------------- /src/components/logout_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(logout_controller). 22 | -compile(export_all). 23 | 24 | index(A) -> 25 | case yaws_arg:get_opaque_val(A, key) of 26 | undefined -> 27 | ok; 28 | Key -> 29 | twoorl_util:auth( 30 | A, fun(Usr) -> 31 | usr:update([{session_key,undefined}], 32 | {id,'=',Usr:id()}) 33 | end), 34 | mnesia:dirty_delete(session, Key) 35 | end, 36 | {response, [yaws_api:setcookie("key", ""), 37 | ewr]}. 38 | -------------------------------------------------------------------------------- /src/components/main_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(main_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | index(A) -> 26 | FeedUrl = <<"/feeds/main/rss">>, 27 | {response, 28 | [{body, [?Data(A, twoorl_util:get_feed_link(FeedUrl, <<"RSS">>)), 29 | {ewc, timeline, show, [A, undefined, [{filter_spam, true}]]}]}, 30 | {phased_vars, 31 | [{header_items, 32 | [{feed_link, <<"rss+xml">>, 33 | <<"Twoorl / Everyone (RSS)">>, 34 | FeedUrl}]}]}]}. 35 | -------------------------------------------------------------------------------- /src/components/main_view.et: -------------------------------------------------------------------------------- 1 | <%@ index([{B, FeedLink}, Data]) %> 2 |
<% B(public_timeline) %>
3 | <% Data %> 4 |
5 | <% FeedLink %> 6 | -------------------------------------------------------------------------------- /src/components/msg.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(msg). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | get_time_since(Msg) -> 26 | twoorl_util:get_time_since(Msg:created_on()). 27 | 28 | get_href(A, Msg) -> 29 | get_href(A, Msg, relative). 30 | 31 | get_href(_A, Msg, relative) -> 32 | [<<"/twoorls/">>, Msg:usr_username(), $/, 33 | integer_to_list(Msg:id())]; 34 | 35 | get_href(A, Msg, absolute) -> 36 | [<<"http://">>, yaws_headers:host(A), 37 | get_href(A, Msg, relative)]. 38 | 39 | process_raw_body(Body) -> 40 | Body1 = twoorl_util:htmlize(Body), 41 | LenDiff = length(Body1) - length(Body), 42 | MaxLen = ?MAX_TWOORL_LEN + LenDiff, 43 | {Body2, BodyNoLinks} = add_tinyurl_links(Body1, MaxLen), 44 | {Body3, RecipientNames} = add_reply_links(lists:flatten(Body2)), 45 | {lists:flatten(Body3), lists:flatten(BodyNoLinks), RecipientNames}. 46 | 47 | add_tinyurl_links(Body, MaxLen) -> 48 | %% regexp:parse("http://[^\s]+") 49 | Re = {concat, 50 | {concat, 51 | {concat, 52 | {concat,{concat,{concat,{concat,104,116},116},112},58}, 53 | 47}, 54 | 47}, 55 | {pclosure,{comp_class," "}}}, 56 | {match, Matches} = regexp:matches(Body, Re), 57 | 58 | %% Perform two passes: once for the web formatting (anchor tags included) 59 | %% and once for RSS formatting (no anchor tags). 60 | %% Remember the tinyurl replacements from the first pass in the 61 | %% second pass. 62 | {Body1, Changes1} = 63 | replace_matches(Body, Matches, fun twoorl_util:get_tinyurl/1, MaxLen), 64 | {Body2, _Changes2} = 65 | replace_matches( 66 | Body, Matches, fun(Url) -> 67 | proplists:get_value(Url, Changes1) 68 | end, MaxLen), 69 | {Body1, Body2}. 70 | 71 | 72 | add_reply_links(Body) -> 73 | %% regexp:parse("@[A-Za-z0-9_]+") 74 | Re = {concat,64, 75 | {pclosure,{char_class,[95,{48,57},{97,122},{65,90}]}}}, 76 | {Body1, Changes} = 77 | replace_matches( 78 | Body, Re, fun([_ | Name] = Val) -> 79 | {usr:get_link(Name, Val, list), Val} 80 | end), 81 | {Body1, [Match || {[_|Match], _Replacement} <- Changes]}. 82 | 83 | 84 | replace_matches(Body, Matches, Fun) -> 85 | replace_matches(Body, Matches, Fun, 999999999). 86 | replace_matches(Body, Matches, Fun, MaxLen) -> 87 | twoorl_util:replace_matches(Body, Matches, Fun, MaxLen). 88 | 89 | get_gravatar_id(Msg) -> 90 | case Msg:usr_gravatar_enabled() of 91 | 1 -> 92 | case Msg:usr_gravatar_id() of 93 | undefined -> 94 | ?DEFAULT_GRAVATAR_ID; 95 | Other -> 96 | Other 97 | end; 98 | _ -> 99 | ?DEFAULT_GRAVATAR_ID 100 | end. 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/components/navbar_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(navbar_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | private() -> 26 | true. 27 | 28 | index(A) -> 29 | Appmod = tl(yaws_arg:appmoddata(A)), 30 | Usr = twoorl_util:get_usr(A), 31 | Username = Usr:username(), 32 | B = twoorl_util:get_bundle(A), 33 | Tabs = 34 | [{"home", <<"home">>, B(home)}, 35 | {"replies", <<"replies">>, B(replies)}, 36 | {"users/" ++ binary_to_list(Username), Username, B(me)}, 37 | {"main", <<"main">>, B(everyone)}], 38 | Links = 39 | lists:map( 40 | fun({Tab, _Href, Title}) when Tab == Appmod -> 41 | Title; 42 | ({_Tab, Href, Title}) -> 43 | erlyweb_html:a(["", Href], Title) 44 | end, Tabs), 45 | {data, Links}. 46 | 47 | -------------------------------------------------------------------------------- /src/components/navbar_view.et: -------------------------------------------------------------------------------- 1 | <%@ index(Links) %> 2 | 3 | 4 | <% links(Links) %> 5 | 6 | 7 | 8 | <%@ links([Link]) %><% Link %> 9 | <%@ links([Link | Rest]) %><% Link %> | <% links(Rest) %> 10 | -------------------------------------------------------------------------------- /src/components/paging_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(paging_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | private() -> 26 | true. 27 | 28 | index(A, Fun) -> 29 | index(A, Fun, undefined). 30 | 31 | index(A, Fun, ResFun) -> 32 | index(A, Fun, ResFun, []). 33 | 34 | 35 | index(A, Fun, ResFun, Opts) -> 36 | {Page, Params} = get_page(A, true), 37 | 38 | PageSize = case proplists:get_value(page_size, Opts) of 39 | undefined -> 40 | ?MAX_PAGE_SIZE; 41 | Other -> 42 | Other 43 | end, 44 | 45 | Ewcs = Fun({limit, (Page - 1)*PageSize, PageSize}), 46 | 47 | Len = length(Ewcs), 48 | Total = proplists:get_value(total, Opts), 49 | NumPages = if Total == undefined -> 50 | if Len < PageSize -> 51 | Page; 52 | true -> 53 | undefined 54 | end; 55 | true -> 56 | ceil(Total/PageSize) 57 | end, 58 | From = (Page-1) * PageSize, 59 | To = From + Len, 60 | From1 = if Len > 0 -> From + 1; true -> From end, 61 | [{data, {Len, 62 | if Total =/= undefined -> 63 | integer_to_list(Total); 64 | true -> 65 | Total 66 | end, 67 | integer_to_list(From1), 68 | integer_to_list(To), 69 | lists:member(show_stats, Opts), 70 | case proplists:get_value(desc, Opts) of 71 | undefined -> <<"item">>; 72 | Other3 -> Other3 73 | end, 74 | get_links(A, NumPages, Page, Params)}}, 75 | if ResFun == undefined -> 76 | Ewcs; 77 | true -> 78 | ResFun(Ewcs) 79 | end]. 80 | 81 | get_page(A) -> 82 | get_page(A, false). 83 | get_page(A, ReturnParams) -> 84 | Params = if is_tuple(A) -> 85 | yaws_api:parse_query(A); 86 | true -> 87 | A 88 | end, 89 | Page = case proplists:get_value("page", Params) of 90 | undefined -> 91 | 1; 92 | Val -> 93 | case catch list_to_integer(Val) of 94 | {'EXIT', _} -> 95 | 1; 96 | Num -> 97 | lists:max([1, Num]) 98 | end 99 | end, 100 | if ReturnParams -> 101 | {Page, Params}; 102 | true -> 103 | Page 104 | end. 105 | 106 | get_links(A, NumPages, Page, Params) -> 107 | Page1 = lists:max([lists:min([Page, NumPages]), 1]), 108 | Params1 = proplists:delete("page", Params), 109 | 110 | %% ugly hack ahead to get around appmod rewriting for the 'users' 111 | %% component 112 | A1 = case yaws_arg:get_opaque_val(A, paging_path) of 113 | undefined -> 114 | A; 115 | Val -> 116 | yaws_arg:appmoddata(A, Val) 117 | end, 118 | BaseUrl1 = erlyweb_util:get_url_prefix(A1), 119 | 120 | BaseUrl2 = 121 | [BaseUrl1, "?", lists:map(fun({Key, Val}) -> [Key,$=,Val,$&] end, 122 | Params1)], 123 | MakeLink = 124 | fun(PageNum) -> 125 | make_link(BaseUrl2, PageNum, 126 | integer_to_list(PageNum)) 127 | end, 128 | MakeWindow = 129 | fun(Min, Max, Tail) -> 130 | Seq = if Max >= Min -> 131 | lists:reverse(lists:seq(Min, Max)); 132 | true -> 133 | [] 134 | end, 135 | lists:foldl( 136 | fun(PageNum, Acc) -> 137 | [MakeLink(PageNum) | Acc] 138 | end, Tail, Seq) 139 | end, 140 | 141 | NextLinks = if NumPages == undefined -> 142 | [make_link(BaseUrl2, Page1 + 1, <<"next >">>)]; 143 | Page1 < NumPages -> 144 | FirstLinks = 145 | if Page1 + ?PAGING_WINDOW < NumPages - 1 -> 146 | [{nolink, <<"...">>}, MakeLink(NumPages)]; 147 | Page1 + ?PAGING_WINDOW < NumPages -> 148 | [MakeLink(NumPages)]; 149 | true -> 150 | [] 151 | end, 152 | MakeWindow( 153 | Page+1, 154 | lists:min([Page1 + ?PAGING_WINDOW, 155 | NumPages]), 156 | FirstLinks); 157 | true -> 158 | [] 159 | end, 160 | NextLinks1 = [{nolink, integer_to_list(Page1)} | NextLinks], 161 | AllLinks = 162 | begin 163 | Links2 = 164 | MakeWindow( 165 | lists:max([1, Page1 - ?PAGING_WINDOW]), Page1 - 1, 166 | NextLinks1), 167 | if Page1 - ?PAGING_WINDOW > 2 -> 168 | [MakeLink(1), {nolink, <<"...">>} | Links2]; 169 | Page1 - ?PAGING_WINDOW > 1 -> 170 | [MakeLink(1) | Links2]; 171 | true -> 172 | Links2 173 | end 174 | end, 175 | AllLinks. 176 | 177 | make_link(BaseUrl, Page, Text) -> 178 | {link, [[BaseUrl, [<<"page=">>, integer_to_list(Page)]]], 179 | Text}. 180 | 181 | limit(Page, PageSize) -> 182 | {limit, (Page - 1)*PageSize, PageSize}. 183 | 184 | 185 | ceil(Num) -> 186 | R = round(Num), 187 | if R < Num -> 188 | R + 1; 189 | true -> 190 | R 191 | end. 192 | -------------------------------------------------------------------------------- /src/components/paging_view.et: -------------------------------------------------------------------------------- 1 | <%@ index([{Num, Total, From, To, ShowStats, Desc, PagingLinks}, Content]) %> 2 | <% if ShowStats -> 3 | [<<"
">>, stats(Num, Desc, From, To, Total), <<"
">>]; 4 | true -> 5 | [] 6 | end %> 7 |
8 | <% Content %> 9 |
10 | <% [show_link(Link) || Link <- PagingLinks] %> 11 | 12 | 13 | <%@ show_link({nolink, Text}) %><% Text %> 14 | <%@ show_link({link, Href, Text}) %><% erlyweb_html:a(Href, Text) %> 15 | 16 | <%@ stats(0, _, _, _, "0") %> 17 | <%@ stats(0, Desc, _, _, _) %>no more <% Desc %>s 18 | <%@ stats(_Num, Desc, "1", "1", Total) %>1 <% Desc %> 19 | <%@ stats(_Num, Desc, From, From, undefined) %><% Desc %> <% From %> 20 | <%@ stats(_Num, Desc, From, From, Total) %><% Desc %> <% From %> out of <% Total %> 21 | <%@ stats(_Num, Desc, From, To, Total) %><% Desc %>s <% From %> to <% To %><% 22 | if Total =/= undefined -> [<<" out of ">>, Total]; 23 | true -> [] 24 | end %> 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/components/register_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(register_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | index(A) -> 26 | case yaws_arg:method(A) of 27 | 'POST' -> 28 | Params = yaws_api:parse_post(A), 29 | {[Username, Email, Password, Password2], Errs} = 30 | erlyweb_forms:validate( 31 | Params, ["username", "email", "password", "password2"], 32 | fun validate/2), 33 | Errs1 = 34 | if Password == Password2 -> 35 | Errs; 36 | true -> 37 | Errs ++ [password_mismatch] 38 | end, 39 | if Errs1 =/= [] -> 40 | [?Data(A, {Username, Email}), 41 | {ewc, ui_msgs, [A, Errs1]}]; 42 | true -> 43 | Usr = register_usr(Username, Email, Password), 44 | login_controller:do_login(A, Usr) 45 | end; 46 | _ -> 47 | [?Data(A, {[], []}), {data, []}] 48 | end. 49 | 50 | validate(Name, Val) -> 51 | case Name of 52 | "username" -> 53 | validate_username(Val); 54 | "password" -> 55 | case length(Val) >= ?MIN_PASSWORD_LENGTH of 56 | true -> 57 | ok; 58 | _ -> 59 | {error, {too_short, Name, 60 | integer_to_list(?MIN_PASSWORD_LENGTH)}} 61 | end; 62 | "email" -> 63 | if Val == [] -> 64 | {error, {missing_field, "email"}}; 65 | true -> 66 | ok 67 | end; 68 | _ -> 69 | ok 70 | end. 71 | 72 | validate_username([]) -> {error, {missing_field, "username"}}; 73 | validate_username(Val) -> 74 | %% regexp:parse("[A-Za-z0-9_]+") 75 | Re = {pclosure,{char_class,[95,{48,57},{97,122},{65,90}]}}, 76 | case regexp:match(Val, Re) of 77 | nomatch -> 78 | {error, {invalid_username, twoorl_util:htmlize(Val)}}; 79 | {match,First,Len} when First =/= 1; 80 | Len =/= length(Val) -> 81 | {error, {invalid_username, twoorl_util:htmlize(Val)}}; 82 | _ -> 83 | case usr:find_first({username,'=',Val}) of 84 | undefined -> 85 | {ok, Val}; 86 | _ -> 87 | {error, {username_taken, Val}} 88 | end 89 | end. 90 | 91 | 92 | register_usr(Username, Email, Password) -> 93 | Usr = usr:new_with([{username, list_to_binary(Username)}, 94 | {email, Email}, 95 | %% not the most secure password storage method, 96 | %% but good enough for now 97 | {password, crypto:sha(Username ++ Password)}]), 98 | usr:save(Usr). 99 | -------------------------------------------------------------------------------- /src/components/register_view.et: -------------------------------------------------------------------------------- 1 | <%@ index([{B, {Username, Email}}, UiMsgs]) %> 2 |
<% B(register_cap) %>
3 | <% UiMsgs %> 4 |
5 |
<% B(username) %>
6 |
7 |
<% B(email) %>
8 |
9 |
<% B(password) %>
10 |
11 |
<% B(password2) %>
12 |
13 |
14 |
15 | <% B(already_member) %> <% B(login_cap) %> 16 | 17 | -------------------------------------------------------------------------------- /src/components/replies_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(replies_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | index(A) -> 26 | twoorl_util:auth( 27 | A, 28 | fun(Usr) -> 29 | Replies = reply:find({usr_id,'=',Usr:id()}, 30 | [{order_by, {created_on, desc}}, 31 | {limit, ?MAX_PAGE_SIZE}]), 32 | MsgIds = [Reply:msg_id() || Reply <- Replies], 33 | Msgs = msg:find({id,in,MsgIds}, {order_by, {created_on, desc}}), 34 | {ewc, timeline, show_msgs, [A, Msgs]} 35 | end). 36 | 37 | -------------------------------------------------------------------------------- /src/components/replies_view.et: -------------------------------------------------------------------------------- 1 | <%@ index(Data) %> 2 |
replies
3 | <% Data %> 4 | -------------------------------------------------------------------------------- /src/components/reply.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(reply). 22 | -compile(export_all). 23 | 24 | save_replies(MsgId, Recipients) -> 25 | Replies = [reply:new_with([{usr_id, Recipient:id()}, {msg_id, MsgId}]) 26 | || Recipient <- Recipients], 27 | reply:insert(Replies). 28 | -------------------------------------------------------------------------------- /src/components/settings_controller.erl: -------------------------------------------------------------------------------- 1 | -module(settings_controller). 2 | -export([index/1]). 3 | -include("twoorl.hrl"). 4 | 5 | index(A) -> 6 | twoorl_util:auth(A, fun(Usr) -> process_request(A, Usr) end). 7 | 8 | 9 | process_request(A, Usr) -> 10 | case yaws_arg:method(A) of 11 | 'POST' -> 12 | Params = yaws_api:parse_post(A), 13 | TwitterEnabled = is_checked("twitter_enabled", Params), 14 | GravatarEnabled = is_checked("gravatar_enabled", Params), 15 | ValidationFun = get_validation_fun(TwitterEnabled), 16 | Background = proplists:get_value("background", Params), 17 | 18 | 19 | {[TwitterUsername, TwitterPassword], Errs} = 20 | erlyweb_forms:validate( 21 | Params, 22 | ["twitter_username", "twitter_password"], 23 | ValidationFun), 24 | Errs1 = verify_twitter_credentials( 25 | TwitterEnabled, TwitterUsername, TwitterPassword), 26 | Errs2 = case Background of 27 | undefined -> Errs1; 28 | "http://" ++ _ -> Errs1; 29 | _ -> [{invalid_url, <<"background image">>} | Errs1] 30 | end, 31 | Errs3 = Errs ++ Errs2, 32 | case Errs3 of 33 | [] -> 34 | Usr2 = 35 | update_settings( 36 | Usr, TwitterUsername, TwitterPassword, 37 | TwitterEnabled, GravatarEnabled, Background), 38 | twoorl_util:update_session(A,Usr2), 39 | {ewr, settings, ["?success=true"]}; 40 | _ -> 41 | [result_data( 42 | A, 43 | TwitterUsername, TwitterPassword, 44 | checked(TwitterEnabled), checked(GravatarEnabled), 45 | str(Background)), 46 | {ewc, ui_msgs, [A, Errs3, []]}] 47 | end; 48 | _ -> 49 | UiMessages = case lists:member({"success", "true"}, 50 | yaws_api:parse_query(A)) of 51 | true -> 52 | [settings_updated]; 53 | false -> 54 | [] 55 | end, 56 | [result_data(A, str(usr:twitter_username(Usr)), 57 | str(usr:twitter_password(Usr)), 58 | checked(usr:twitter_enabled(Usr)), 59 | checked(usr:gravatar_enabled(Usr)), 60 | str(usr:background(Usr))), 61 | {ewc, ui_msgs, [A, [], UiMessages]}] 62 | end. 63 | 64 | result_data(A, TwitterUsername, TwitterPassword, TwitterEnabled, 65 | GravatarEnabled, Background) -> 66 | ?Data(A, {TwitterUsername, TwitterPassword, TwitterEnabled, 67 | GravatarEnabled, Background}). 68 | 69 | 70 | get_validation_fun(true) -> 71 | fun(Field, Val) -> 72 | case Val of 73 | [] -> 74 | FName = case Field of 75 | "twitter_username" -> 76 | "Twitter username"; 77 | "twitter_password" -> 78 | "Twitter password" 79 | end, 80 | {error, {missing_field, FName}}; 81 | _ -> 82 | ok 83 | end 84 | end; 85 | get_validation_fun(_) -> 86 | fun(_Field, _Val) -> 87 | ok 88 | end. 89 | 90 | verify_twitter_credentials(_, [], _) -> []; 91 | verify_twitter_credentials(_, _, []) -> []; 92 | verify_twitter_credentials(true, Username, Password) -> 93 | case twitter_client:account_verify_credentials(Username, Password, []) of 94 | true -> []; 95 | false -> [twitter_unauthorized] 96 | end; 97 | verify_twitter_credentials(_, _, _) -> []. 98 | 99 | update_settings(Usr, TwitterUsername, TwitterPassword, TwitterEnabled, 100 | GravatarEnabled, Background) -> 101 | Usr1 = usr:set_fields( 102 | Usr, 103 | [{twitter_username, TwitterUsername}, 104 | {twitter_password, TwitterPassword}, 105 | {twitter_enabled, bool_to_int(TwitterEnabled)}, 106 | {gravatar_enabled, bool_to_int(GravatarEnabled)}, 107 | {background, Background}]), 108 | Usr2 = Usr1:save(), 109 | LastGravatarStatus = Usr:gravatar_enabled(), 110 | if GravatarEnabled == LastGravatarStatus -> 111 | ok; 112 | true -> 113 | %% TODO consider doing this in a 114 | %% background processes 115 | msg:update([{usr_gravatar_enabled, 116 | bool_to_int(GravatarEnabled)}], 117 | {usr_id,'=',Usr:id()}) 118 | end, 119 | Usr2. 120 | 121 | 122 | str(undefined) -> []; 123 | str(Val) when is_list(Val) -> list_to_binary(Val); 124 | str(Val) -> Val. 125 | 126 | checked(0) -> []; 127 | checked(false) -> []; 128 | checked(1) -> checked1(); 129 | checked(true) -> checked1(); 130 | checked(undefined) -> []. 131 | 132 | checked1() -> 133 | <<"checked=\"checked\"">>. 134 | 135 | is_checked(Param, Params) -> 136 | proplists:get_value(Param, Params) == "on". 137 | 138 | bool_to_int(false) -> 0; 139 | bool_to_int(true) -> 1. 140 | -------------------------------------------------------------------------------- /src/components/settings_view.et: -------------------------------------------------------------------------------- 1 | <%@ index([{B, {TwitterUsername, TwitterPassword, TwitterEnabledChecked, 2 | GravatarEnabledChecked, Background}}, 3 | UiMsgs]) %> 4 |
<% B(settings_cap) %>
5 | <% UiMsgs %> 6 |
7 |
8 |
Gravatar
9 |
<% B(use_gravatar) %> />
10 |
11 |
12 |
<% B(profile_bg) %>
13 |
<% B(profile_bg_help) %>
14 |
15 |
16 |
17 |
Twitter
18 |
<% B(twitter_help) %>

19 |
<% B(twitter_username) %>
20 |
21 |
<% B(twitter_password) %>
22 |
23 |
<% B(twitter_auto) %> />
24 |
25 |

26 |
27 |
28 | 29 | -------------------------------------------------------------------------------- /src/components/stats_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Nick Gerakines, 2008 17 | %% 18 | %% @author Nick Gerakines [http://blog.socklabs.com/] 19 | %% @copyright Nick Gerakines, 2008 20 | 21 | -module(stats_controller). 22 | -compile(export_all). 23 | 24 | %% disabled for now 25 | private() -> 26 | true. 27 | 28 | index(_A) -> 29 | Registration = twoorl_stats:call({graph, registration, 7}), 30 | Updates = twoorl_stats:call({graph, twoot, 7}), 31 | {data, {Registration, Updates}}. 32 | -------------------------------------------------------------------------------- /src/components/stats_view.et: -------------------------------------------------------------------------------- 1 | <%@ index({Registration, Updates}) %> 2 |
3 |
4 | 5 |
6 | 9 |
10 |
11 |
12 | 13 |
14 | 17 |
18 | -------------------------------------------------------------------------------- /src/components/timeline_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(timeline_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | private() -> 26 | true. 27 | 28 | index(_A) -> 29 | {data, undefined}. 30 | 31 | show(A) -> 32 | show(A, undefined). 33 | 34 | show(A, Usr) when not is_list(Usr), Usr =/= undefined -> 35 | show(A, [Usr:id()]); 36 | 37 | show(A, UserIds) -> 38 | show(A, UserIds, []). 39 | 40 | show(A, UserIds, Opts) -> 41 | OrderBy = {order_by, {created_on, desc}}, 42 | 43 | Where = case proplists:get_value(filter_spam, Opts) of 44 | true -> 45 | {'not', {spam,'=',1}}; 46 | _ -> 47 | true 48 | end, 49 | %% this function is a prime optimization candidate 50 | Where1 = 51 | if UserIds =/= undefined -> 52 | {'and', [{usr_id, in, UserIds}, Where]}; 53 | true -> 54 | Where 55 | end, 56 | Total = msg:count('*', Where1), 57 | 58 | {replace, 59 | {ewc, paging, 60 | [A, fun(Limit) -> 61 | case Where1 of 62 | undefined -> 63 | msg:find_with([OrderBy, Limit]); 64 | _ -> 65 | msg:find(Where1, [OrderBy, Limit]) 66 | end 67 | end, 68 | fun(Msgs) -> 69 | {ewc, timeline, show_msgs, [A, Msgs, Opts]} 70 | end, 71 | [{total, Total}]]}}. 72 | 73 | show_msgs(A, Msgs) -> 74 | show_msgs(A, Msgs, []). 75 | 76 | show_msgs(A, Msgs, Opts) -> 77 | Opts1 = 78 | case proplists:get_value(big_first, Opts) of 79 | undefined -> 80 | Opts; 81 | true -> 82 | [{is_big, true} | Opts] 83 | end, 84 | if Msgs == [] -> 85 | []; 86 | true -> 87 | [{ewc, timeline, show_msg, [A, hd(Msgs), Opts1]} | 88 | [{ewc, timeline, show_msg, [A, Msg, Opts]} || Msg <- tl(Msgs)]] 89 | end. 90 | 91 | show_msg(A, Msg) -> 92 | show_msg(A, Msg, []). 93 | 94 | show_msg(A, Msg, Opts) -> 95 | Username = Msg:usr_username(), 96 | {Icon, Userlink} = 97 | case proplists:get_value(hide_user, Opts) of 98 | true -> 99 | {[], []}; 100 | _ -> 101 | GravatarId = 102 | msg:get_gravatar_id(Msg), 103 | {usr:get_icon_link(Username, GravatarId), 104 | usr:get_link(Username)} 105 | end, 106 | CreatedOn = twoorl_util:i18n(A, msg:get_time_since(Msg)), 107 | 108 | IsBig = proplists:get_value(is_big, Opts) == true, 109 | 110 | {data, {Username, Icon, Userlink, 111 | Msg:body(), msg:get_href(A, Msg), CreatedOn, IsBig}}. 112 | -------------------------------------------------------------------------------- /src/components/timeline_view.et: -------------------------------------------------------------------------------- 1 | <%@ show_msg({Username, Gravatar, Userlink, Body, Href, Time, IsBig}) %> 2 |
>; true -> [] end %>"> 3 | 4 | 5 | <% if Gravatar =/= undefined -> gravatar(Gravatar); true -> [] end %> 6 | 9 | 10 |
7 | <% if Userlink =/= undefined -> user_link(Userlink); true -> [] end %> 8 | <% Body %> <% Time %>
11 |
12 | 13 | <%@ gravatar(Gravatar) %><% Gravatar %> 14 | <%@ user_link(UserLink) %><% UserLink %> 15 | 16 | 17 | <%@ show_msgs(Data) %> 18 |
<% Data %>
19 | -------------------------------------------------------------------------------- /src/components/twoorls_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(twoorls_controller). 22 | -compile(export_all). 23 | 24 | catch_all(A, [_Username, Id]) -> 25 | %% currently, we ignore the username, but we keep it as a parameter 26 | %% for future user-based sharding 27 | case msg:find_id(list_to_integer(Id)) of 28 | undefined -> 29 | exit({not_found, Id}); 30 | Msg -> 31 | {data, {usr:get_link(Msg:usr_username()), 32 | twoorl_util:gravatar_icon(Msg:usr_gravatar_id()), 33 | Msg:body(), 34 | twoorl_util:i18n(A, Msg:get_time_since())}} 35 | end. 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/components/twoorls_view.et: -------------------------------------------------------------------------------- 1 | <%@ catch_all({Userlink, Icon, Body, Time}) %> 2 |
3 |
<% Body %>
4 | 13 |
14 | -------------------------------------------------------------------------------- /src/components/ui_msgs_controller.erl: -------------------------------------------------------------------------------- 1 | -module(ui_msgs_controller). 2 | -export([index/2, index/3, private/0]). 3 | -include("twoorl.hrl"). 4 | 5 | private() -> 6 | true. 7 | 8 | index(A, Errs) -> 9 | index(A, Errs, []). 10 | 11 | index(A, Errs, Msgs) -> 12 | B = twoorl_util:get_bundle(A), 13 | Msgs1 = lists:map(B, Msgs), 14 | Errs1 = lists:map(B, Errs), 15 | Errs2 = [{error, Err} || Err <- Errs1], 16 | {data, Msgs1 ++ Errs2}. 17 | -------------------------------------------------------------------------------- /src/components/ui_msgs_view.et: -------------------------------------------------------------------------------- 1 | <%@ index(Msgs) %> 2 |
3 | <% [ui_msg(M) || M <- Msgs] %> 4 |
5 | 6 | <%@ ui_msg({error, Msg}) %>
* <% Msg %>
7 | <%@ ui_msg(Msg) %>
* <% Msg %>
8 | -------------------------------------------------------------------------------- /src/components/user_icon_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(user_icon_controller). 22 | -compile(export_all). 23 | 24 | private() -> 25 | true. 26 | 27 | index(_A, Usr) -> 28 | {data, {usr:get_link(Usr), 29 | usr:get_icon(Usr, true)}}. 30 | -------------------------------------------------------------------------------- /src/components/user_icon_view.et: -------------------------------------------------------------------------------- 1 | <%@ index({UserLink, GravatarIcon}) %> 2 |
3 |
<% GravatarIcon %>
4 |
<% UserLink %>
5 |
6 | -------------------------------------------------------------------------------- /src/components/user_list_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(user_list_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | private() -> 26 | true. 27 | 28 | show_related(A, Username, IsFollowing) -> 29 | case usr:find_first({username,'=',Username}) of 30 | undefined -> 31 | exit({no_such_user, Username}); 32 | Usr -> 33 | {Field1, Field2} = 34 | if IsFollowing -> 35 | {usr_id1, usr_id2}; 36 | true -> 37 | {usr_id2, usr_id1} 38 | end, 39 | 40 | %% TODO cache this data 41 | Total = following:count('*', {Field1,'=',Usr:id()}), 42 | 43 | {replace, 44 | {ewc, paging, 45 | [A, 46 | fun(Limit) -> 47 | following:find( 48 | {Field1,'=',Usr:id()}, 49 | Limit) 50 | end, 51 | fun(Followings) -> 52 | Usr2Ids = 53 | [following:Field2(Following) || 54 | Following <- Followings], 55 | Users = usr:find({id,in,Usr2Ids}), 56 | {ewc, user_list, show, 57 | [A, Usr, IsFollowing, Users]} 58 | end, 59 | [{total, Total}, 60 | {page_size, ?FOLLOWING_PAGE_SIZE}]]}} 61 | end. 62 | 63 | show(A, Usr, IsFollowing, Friends) -> 64 | Userlink = Usr:get_link(), 65 | B = twoorl_util:get_bundle(A), 66 | Title = if IsFollowing -> 67 | B({friends_of, Userlink}); 68 | true -> 69 | B({followers_of, Userlink}) 70 | end, 71 | FriendIcons = 72 | if Friends == [] -> 73 | Username = Usr:username(), 74 | if IsFollowing -> 75 | {data, B({no_friends, Username})}; 76 | true -> 77 | {data, B({no_followers, Username})} 78 | end; 79 | true -> 80 | FriendIconEwcs = 81 | [{ewc, user_icon, [A, Friend]} || Friend <- Friends], 82 | {ewc, grid, [A, FriendIconEwcs, 5]} 83 | end, 84 | [{data, {Usr:get_icon(), Title}}, FriendIcons]. 85 | 86 | -------------------------------------------------------------------------------- /src/components/user_list_view.et: -------------------------------------------------------------------------------- 1 | <%@ show([{Icon, Title}, FriendIcons]) %> 2 |
3 | 4 | 5 | 6 | 7 | 8 |
<% Icon %><% Title %>
9 |
10 | <% FriendIcons %> 11 | -------------------------------------------------------------------------------- /src/components/users_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(users_controller). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | catch_all(A, [Username]) -> 26 | case usr:find_first({username,'=',Username}) of 27 | undefined -> 28 | ?Data(A, {no_user, Username}); 29 | Usr -> 30 | ToFollow = 31 | case twoorl_util:get_usr(A) of 32 | undefined = Val -> Val; 33 | Me -> 34 | case Me:id() == Usr:id() of 35 | true -> 36 | undefined; 37 | _ -> 38 | Following = following:find_first( 39 | {'and', 40 | [{usr_id1,'=',Me:id()}, 41 | {usr_id2,'=',Usr:id()}]}), 42 | Following == undefined 43 | end 44 | end, 45 | FeedUrl = usr:get_feed_url(Usr, <<"rss">>), 46 | {response, 47 | [{body, 48 | [?Data(A, {Username, ToFollow, usr:get_icon(Usr), 49 | twoorl_util:get_feed_link(FeedUrl, <<"RSS">>)}), 50 | {ewc, timeline, show, [A, [Usr:id()], 51 | [{big_first, true}, 52 | {hide_user, true}]]}]}, 53 | {phased_vars, [{background, Usr:background()}, 54 | {header_items, 55 | [{feed_link, <<"rss+xml">>, 56 | [Username, <<" (RSS)">>], 57 | FeedUrl}]}]}]} 58 | end. 59 | -------------------------------------------------------------------------------- /src/components/users_view.et: -------------------------------------------------------------------------------- 1 | <%@ catch_all({B, Err}) %> 2 | <% B(Err) %> 3 | 4 | <%@ catch_all([{B, {Username, ToFollow, Gravatar, FeedLink}}, Data]) %> 5 |
6 | 7 | 8 | 9 | 10 | 14 | 15 |
<% Gravatar %>
<% B({timeline_of, Username}) %>
16 | <% if ToFollow =/= undefined -> to_follow(B, ToFollow, Username); 17 | true -> [] end %> 18 |
19 | <% Data %> 20 |
21 | <% FeedLink %> 22 | 23 | <%@ to_follow(B, Val, Username) %> 24 | 28 | -------------------------------------------------------------------------------- /src/components/usr.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(usr). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | get_icon(Usr) -> 26 | get_icon(Usr, false). 27 | 28 | get_icon(Usr, AsLink) -> 29 | GravatarId = 30 | case usr:gravatar_enabled(Usr) of 31 | 1 -> 32 | twoorl_util:gravatar_id(usr:email(Usr)); 33 | 0 -> 34 | ?DEFAULT_GRAVATAR_ID 35 | end, 36 | if AsLink -> 37 | get_icon_link(Usr:username(), GravatarId); 38 | true -> 39 | twoorl_util:gravatar_icon(GravatarId) 40 | end. 41 | 42 | get_icon_link(Username, GravatarId) -> 43 | get_link(Username, twoorl_util:gravatar_icon(GravatarId)). 44 | 45 | get_timeline_usr_ids(Usr) -> 46 | Followings = following:find({usr_id1,'=',Usr:id()}), 47 | FollowingIds = [Following:usr_id2() || Following <- Followings], 48 | [Usr:id() | FollowingIds]. 49 | 50 | get_feed_url(Usr, Format) -> 51 | [<<"/feeds/users/">>, Usr:username(), $/, Format]. 52 | 53 | 54 | get_link(Usr) when is_tuple(Usr) -> 55 | get_link(Usr:username()); 56 | get_link(Username) -> 57 | get_link(Username, Username). 58 | 59 | get_link(Username, Text) -> 60 | get_link(Username, Text, iolist). 61 | 62 | get_link(Username, Text, iolist) -> 63 | [<<">, Username, <<"\">">>, Text, <<"">>]; 64 | get_link(Username, Text, list) -> 65 | ["", Text, ""]. 66 | 67 | -------------------------------------------------------------------------------- /src/twitter.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(twitter). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | post(Url, Username, Password, Params) when is_binary(Username) -> 26 | post(Url, binary_to_list(Username), Password, Params); 27 | 28 | post(Url, Username, Password, Params) when is_binary(Password) -> 29 | post(Url, Username, binary_to_list(Password), Params); 30 | 31 | post(Url, Username, Password, Params) -> 32 | Encoded = binary_to_list( 33 | base64:encode(Username ++ ":" ++ Password)), 34 | Headers = 35 | [{"Authorization", "Basic " ++ Encoded}], 36 | ContentType = "application/x-www-form-urlencoded", 37 | Body = twoorl_util:join([[Field,$=,yaws_api:url_encode(Val)] || 38 | {Field,Val} <- Params], "&"), 39 | http:request( 40 | post, 41 | {Url, Headers, ContentType, iolist_to_binary(Body)}, [], []). 42 | 43 | update(Username, Password, Status) -> 44 | post("http://twitter.com/statuses/update.json", Username, Password, 45 | [{"status", Status}]). 46 | 47 | verify_credentials(Username, Password) -> 48 | case post("http://twitter.com/account/verify_credentials.json", 49 | Username, Password, []) of 50 | {ok, {{_, Status, _}, _Headers, _Body}} -> 51 | case Status of 52 | 200 -> 53 | ok; 54 | 401 -> 55 | {error, unauthorized}; 56 | _ -> 57 | {error, {unexpected_status, Status}} 58 | end; 59 | Res -> 60 | {error, Res} 61 | end. 62 | -------------------------------------------------------------------------------- /src/twoorl.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(twoorl). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | -include("twoorl_app.hrl"). 25 | 26 | start(_Type, _Args) -> 27 | twoorl_sup:start_link([]). 28 | 29 | start_phase(mysql, _, _) -> 30 | {ok, DBConfig} = application:get_env(twoorl, dbconns), 31 | [mysql_connect(PoolSize, Hostname, User, Password, Database) 32 | || {Hostname, User, Password, Database, PoolSize} <- DBConfig], 33 | ok; 34 | 35 | start_phase(compile, _, _) -> 36 | twoorl:compile(), 37 | ok; 38 | 39 | %% Having the mnesia store on a separate but connected node with a module 40 | %% to handle its maintenance would move a lot of this foo out of the 41 | %% application stack. Eventually that really needs to happen. -- nkg 42 | start_phase(mnesia, _, _) -> 43 | %% Mnesia should have been started already, because of that the schema 44 | %% is in memory if the schema doesn't already exist on disc. If so we 45 | %% change the type so that it writes to the mnesia dir we set. -- nkg 46 | case mnesia:table_info(schema, storage_type) of 47 | ram_copies -> 48 | mnesia:change_table_copy_type(schema, node(), disc_copies); 49 | _ -> 50 | ok 51 | end, 52 | ExistingTables = mnesia:system_info(tables) -- [schema], 53 | {ok, Tables} = application:get_env(twoorl, tables), 54 | [create_table(Table) || 55 | Table <- Tables, not lists:member(Table, ExistingTables)], 56 | ok. 57 | 58 | create_table(session) -> 59 | mnesia:create_table(session, [{attributes, record_info(fields, session)}]), 60 | ok. 61 | 62 | mysql_connect(PoolSize, Hostname, User, Password, Database) -> 63 | erlydb:start( 64 | mysql, [{hostname, Hostname}, 65 | {username, User}, 66 | {password, Password}, 67 | {database, Database}, 68 | {logfun, fun twoorl_util:log/4}]), 69 | lists:foreach( 70 | fun() -> 71 | mysql:connect(erlydb_mysql, Hostname, undefined, User, Password, 72 | Database, true) 73 | end, lists:seq(1, PoolSize - 1)). 74 | 75 | compile() -> 76 | compile([]). 77 | 78 | compile_dev() -> 79 | compile([{auto_compile, true}]). 80 | 81 | compile_update() -> 82 | compile([{last_compile_time, auto}]). 83 | 84 | compile(Opts) -> 85 | erlyweb:compile(compile_dir(default), 86 | [{erlydb_driver, mysql}, {erlydb_timeout, 20000} | Opts]). 87 | 88 | compile_dir(auto) -> 89 | {ok, CWD} = file:get_cwd(), CWD; 90 | compile_dir(default) -> 91 | ?APP_PATH; 92 | compile_dir(appconfig) -> 93 | {ok, CDir} = application:get_env(twoorl, compile_dir), 94 | CDir; 95 | compile_dir(Dir) -> 96 | Dir. 97 | 98 | %% --- The rest of these functions will be deprecated --- %% 99 | 100 | start() -> 101 | application:start(inets), 102 | init_mnesia(), 103 | TablesInfo = [{session, [{attributes, record_info(fields, session)}]}], 104 | create_mnesia_tables(TablesInfo), 105 | init_mysql(), 106 | compile(). 107 | 108 | init_mnesia() -> 109 | ?L("creating schema"), 110 | case mnesia:create_schema([node()]) of 111 | {error, {_,{already_exists, _}}} -> 112 | ?L("schema already exists"); 113 | {error, Err} -> 114 | ?L("schema creation error, aborting"), 115 | exit(Err); 116 | ok -> 117 | ok 118 | end, 119 | case mnesia:start() of 120 | {error, Err1} -> 121 | ?L("error starting mnesia"), 122 | exit(Err1); 123 | ok -> ok 124 | end. 125 | 126 | create_mnesia_tables(TablesInfo) -> 127 | ExistingTables = mnesia:system_info(tables) -- [schema], 128 | TablesToCreate = [TableInfo || TableInfo = {Name, _Atts} <- TablesInfo, 129 | not lists:member(Name, ExistingTables)], 130 | lists:foreach( 131 | fun({TableName, Atts}) -> 132 | create_table(TableName, Atts) 133 | end, TablesToCreate), 134 | 135 | ?L({"waiting for tables", ExistingTables}), 136 | case mnesia:wait_for_tables(ExistingTables, 20000) of 137 | {timeout, RemainingTabs} -> 138 | exit({mnesia_timeout, RemainingTabs}); 139 | ok -> 140 | ok 141 | end. 142 | 143 | create_table(Table, Def) -> 144 | ?L("creating table: " ++ atom_to_list(Table)), 145 | case mnesia:create_table(Table, Def) of 146 | {atomic, ok} -> ok; 147 | {aborted, Err2} -> 148 | ?L("create table error"), 149 | exit(Err2) 150 | end. 151 | 152 | init_mysql() -> 153 | erlydb:start(mysql, 154 | [{hostname, ?DB_HOSTNAME}, 155 | {username, ?DB_USERNAME}, {password, ?DB_PASSWORD}, 156 | {database, ?DB_DATABASE}, 157 | {logfun, fun log/4}]), 158 | lists:foreach( 159 | fun(_) -> 160 | mysql:connect(erlydb_mysql, ?DB_HOSTNAME, undefined, 161 | ?DB_USERNAME, ?DB_PASSWORD, ?DB_DATABASE, true) 162 | end, lists:seq(1, ?DB_POOL_SIZE)). 163 | 164 | log(Module, Line, Level, FormatFun) -> 165 | twoorl_util:log(Module, Line, Level, FormatFun). 166 | -------------------------------------------------------------------------------- /src/twoorl.hrl: -------------------------------------------------------------------------------- 1 | -define(L(Msg), io:format("~p:~b ~p ~n", [?MODULE, ?LINE, Msg])). 2 | 3 | -record(session, {key, value}). 4 | 5 | -define(MIN_PASSWORD_LENGTH, 6). 6 | -define(DEFAULT_KEY_SIZE, 20). 7 | -define(MAX_PAGE_SIZE, 20). 8 | -define(MAX_FOLLOWING_BOX_SIZE, 20). 9 | -define(PAGING_WINDOW, 3). 10 | -define(MAX_TWOORL_LEN, 140). 11 | -define(MAX_MSG_SIZE, 10000). 12 | -define(FOLLOWING_PAGE_SIZE, 40). 13 | 14 | -define(TWITTER_NOT_SENT, 0). 15 | -define(TWITTER_SENT_PENDING, 1). 16 | -define(TWITTER_SENT_OK, 2). 17 | -define(TWITTER_SENT_ERR, 3). 18 | -define(DEFAULT_GRAVATAR_ID, <<"98dbdc9e0d214030d3ee1c77c502248f">>). 19 | -define(DEFAULT_BACKGROUND, <<"/static/bg1.jpg">>). 20 | 21 | -define(Debug(Msg, Params), 22 | twoorl_util:log(?MODULE, ?LINE, debug, fun() -> {Msg, Params} end)). 23 | -define(Info(Msg, Params), 24 | twoorl_util:log(?MODULE, ?LINE, info, fun() -> {Msg, Params} end)). 25 | -define(Warn(Msg, Params), 26 | twoorl_util:log(?MODULE, ?LINE, warn, fun() -> {Msg, Params} end)). 27 | -define(Error(Msg, Params), 28 | twoorl_util:log(?MODULE, ?LINE, error, fun() -> {Msg, Params} end)). 29 | 30 | -define(Data(A, Data), {data, {twoorl_util:get_bundle(A), Data}}. 31 | -------------------------------------------------------------------------------- /src/twoorl_app.hrl: -------------------------------------------------------------------------------- 1 | %% We really need to move this stuff out of this file -- nkg. 2 | -define(DB_HOSTNAME, "localhost"). 3 | -define(DB_USERNAME, "root"). 4 | -define(DB_PASSWORD, "password"). 5 | -define(DB_DATABASE, "twoorl"). 6 | -define(DB_POOL_SIZE, 50). 7 | %-define(APP_PATH, "/home/yariv/apps//twoorl"). 8 | -define(APP_PATH, "/Users/yariv/apps/twoorl"). 9 | -------------------------------------------------------------------------------- /src/twoorl_app_controller.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(twoorl_app_controller). 22 | -export([hook/1]). 23 | -include("twoorl.hrl"). 24 | 25 | hook(A) -> 26 | A1 = normalize_appmoddata(A), 27 | case erlyweb:get_initial_ewc({ewc, A1}) of 28 | {page, "/"} -> start(A1); 29 | {page, "/static" ++ _} = Ewc -> Ewc; 30 | {page, "/favicon.ico"} -> {page, "/static/favicon.ico"}; 31 | {page, [$/ | Username] = Path} -> 32 | A2 = yaws_arg:appmoddata(A1, "/users/" ++ Username), 33 | A3 = yaws_arg:add_to_opaque(A2, {paging_path, Path}), 34 | start(A3); 35 | 36 | %% redirect user urls from "/users/[Username]" to "/[Username]" 37 | {ewc, users_controller, users_view, catch_all, 38 | [_, [Username]]} -> 39 | {response, [{redirect_local, {any_path, [$/|Username]}, 301}]}; 40 | _Ewc -> 41 | start(A1) 42 | end. 43 | 44 | start(A) -> 45 | {A2, LoggedIn} = auth(A), 46 | Appmod = yaws_arg:appmoddata(A2), 47 | case Appmod of 48 | "/api" ++ _ -> 49 | {ewc, A2}; 50 | "/feeds" ++ _ -> 51 | {ewc, A2}; 52 | "/" when LoggedIn == true -> 53 | {ewr, home}; 54 | _ -> 55 | Appmod1 = case Appmod of 56 | "/" -> 57 | "/main"; 58 | _ -> 59 | Appmod 60 | end, 61 | A3 = yaws_arg:appmoddata(A2, Appmod1), 62 | {phased, {ewc, A3}, 63 | fun(_Ewc, Data, PhasedVars) -> 64 | {ewc, html_container, 65 | [A3, 66 | {ewc, layout, [A3, {data, Data}]}, 67 | PhasedVars]} 68 | end} 69 | end. 70 | 71 | %% Populate the arg's opaque field with {session, Session} if the 72 | %% request's "key" cookie value could be found in mnesia. 73 | auth(A) -> 74 | case erlyweb_util:get_cookie("key", A) of 75 | undefined -> 76 | {A, false}; 77 | Val -> 78 | Key = list_to_binary(Val), 79 | case lookup(Key) of 80 | undefined -> 81 | {A, false}; 82 | Session -> 83 | {yaws_arg:add_all_to_opaque( 84 | A, 85 | [{key, Key}, {session, Session}]), true} 86 | end 87 | end. 88 | 89 | lookup(Key) -> 90 | case mnesia:dirty_read(session, Key) of 91 | [] -> 92 | case usr:find_first({session_key,'=',Key}) of 93 | undefined -> 94 | undefined; 95 | Usr -> 96 | Session = #session{key=Key, value=Usr}, 97 | mnesia:dirty_write(Session), 98 | Session 99 | end; 100 | [Session] -> 101 | Session; 102 | Other -> 103 | exit({unexpected_result, Other}) 104 | end. 105 | 106 | %% to avoid annoying Yaws inconsistencies 107 | normalize_appmoddata(A) -> 108 | Val1 = 109 | case yaws_arg:appmoddata(A) of 110 | [] -> 111 | "/"; 112 | Val = [$/ | _] -> 113 | Val; 114 | Val -> 115 | [$/ | Val] 116 | end, 117 | yaws_arg:appmoddata(A, Val1). 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/twoorl_errs.et: -------------------------------------------------------------------------------- 1 | <%@ show(Errs) %> 2 | <% if Errs =/= [] -> errs([err1(Err) || Err <- Errs]); true -> [] end %> 3 | 4 | <%@ errs(Errs) %>
<% Errs %>
5 | <%@ err1(Err) %>
* <% err(Err) %>
6 | 7 | <%@ err({missing_field, Field}) %>the <% Field %> field is required 8 | <%@ err({invalid_username, Val}) %>the username '<% Val %>' is taken 9 | <%@ err(invalid_login) %>invalid username or password 10 | <%@ err(password_too_short) %>the password you entered is too short 11 | <%@ err(password_mismatch) %>the password verification didn't match 12 | 13 | -------------------------------------------------------------------------------- /src/twoorl_migrations.erl: -------------------------------------------------------------------------------- 1 | 2 | %% Implements migration scripts 3 | -module(twoorl_migrations). 4 | -compile(export_all). 5 | -include("twoorl.hrl"). 6 | 7 | m6() -> 8 | Msgs = msg:find(), 9 | {ok, Re} = regexp:parse("http://"), 10 | lists:foreach( 11 | fun(Msg) -> 12 | BodyRaw = binary_to_list(msg:body_raw(Msg)), 13 | {Body1, BodyNoLinks, _Names} = 14 | msg:process_raw_body(BodyRaw), 15 | HasLinks = regexp:matches(BodyRaw, Re) =/= {match, []}, 16 | msg:update([{body_nolinks, lists:flatten(BodyNoLinks)}, 17 | {body, lists:flatten(Body1)}], {id,'=',Msg:id()}), 18 | if HasLinks -> 19 | receive 20 | after 1000 -> ok 21 | end; 22 | true -> 23 | ok 24 | end 25 | end, Msgs). 26 | 27 | %% change user urls from /users/[Username] to /[Username] 28 | m8() -> 29 | Msgs = msg:find(), 30 | {ok, Re} = regexp:parse("/users/"), 31 | lists:foreach( 32 | fun(Msg) -> 33 | Body = binary_to_list(Msg:body()), 34 | {ok, Body1, _N} = regexp:gsub(Body, Re, "/"), 35 | 36 | msg:update([{body, Body1}], {id,'=',Msg:id()}) 37 | end, Msgs). 38 | 39 | 40 | m9() -> 41 | Users = usr:find({spammer,'=',1}), 42 | lists:foreach( 43 | fun(Usr) -> 44 | msg:update([{spam, 1}], {usr_id,'=',Usr:id()}) 45 | end, Users). 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/twoorl_snippets.et: -------------------------------------------------------------------------------- 1 | <%@ google_analytics() %> -------------------------------------------------------------------------------- /src/twoorl_stats.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Nick Gerakines, 2008 17 | %% 18 | %% @author Nick Gerakines [http://blog.socklabs.com/] 19 | %% @copyright Nick Gerakines, 2008 20 | %% @doc Provides a mechanism to track stats in a simple way. 21 | -module(twoorl_stats). 22 | -behaviour(gen_server). 23 | 24 | -author("Nick Gerakines "). 25 | -version("0.1"). 26 | 27 | -compile(export_all). 28 | 29 | -export([ 30 | init/1, terminate/2, code_change/3, 31 | handle_call/3, handle_cast/2, handle_info/2]). 32 | 33 | -include_lib("stdlib/include/qlc.hrl"). 34 | -include("twoorl.hrl"). 35 | 36 | start() -> 37 | gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). 38 | 39 | call(Args) -> 40 | ensure_started(), 41 | gen_server:call(twoorl_stats, Args). 42 | 43 | cast(Args) -> 44 | ensure_started(), 45 | gen_server:cast(twoorl_stats, Args). 46 | 47 | bucket_key(Name, {_, {YY, MM, DD}}) -> 48 | bucket_key(Name, {YY, MM, DD}); 49 | bucket_key(Name, {YY, MM, DD}) -> 50 | lists:flatten(io_lib:format("~s-~w-~w-~w",[Name, YY, MM, DD])). 51 | 52 | google_chart_sparkline(Records, Background) -> 53 | Data = lists:foldl(fun(A, "") -> A; (A, Acc) -> Acc ++ "," ++ A end, 54 | "", [integer_to_list(X)|| X <- Records]), 55 | % Min = lists:min(Records) - 2, 56 | % Max = lists:max(Records) + 2, 57 | lists:concat(["http://chart.apis.google.com/chart?chs=400x200&cht=bvs" 58 | "&chd=t:", Data, "&chco=4d89f9&chf=&chf=bg,s,", 59 | Background ,"|c,s,", Background]). 60 | 61 | google_chart_sparkline(Records) -> 62 | twoorl_stats:google_chart_sparkline(Records, "000000"). 63 | 64 | %% - 65 | %% gen_server functions 66 | 67 | init([]) -> 68 | dets:open_file(twoorl_stats, []). 69 | 70 | handle_call({info}, _From, State) -> 71 | {reply, State, State}; 72 | 73 | handle_call({stats, Name, Range}, _From, State) when is_integer(Range) -> 74 | Now = calendar:datetime_to_gregorian_seconds( 75 | calendar:now_to_universal_time(erlang:now())), 76 | Keys = [begin 77 | Seconds = Now - (60 * 60 * 24 * X), 78 | {Date, _} = calendar:gregorian_seconds_to_datetime(Seconds), 79 | {Date, bucket_key(Name, Date)} 80 | end || X <- lists:seq(0, Range)], 81 | Data = [case dets:lookup(twoorl_stats, Key) of 82 | [{Key, Date, Count}] -> {Date, Count}; 83 | _ -> {Date, 0} 84 | end || {Date, Key} <- Keys], 85 | {reply, Data, State}; 86 | 87 | handle_call({graph, Name, Range}, From, State) when is_integer(Range) -> 88 | {_, Stats, _} = twoorl_stats:handle_call( 89 | {stats, Name, Range}, From, State), 90 | ImgUrl = twoorl_stats:google_chart_sparkline( 91 | lists:reverse([Y || {X, Y} <- Stats])), 92 | {reply, ImgUrl, State}; 93 | 94 | handle_call(stop, _From, State) -> {stop, normalStop, State}; 95 | 96 | handle_call(_, _From, State) -> {noreply, ok, State}. 97 | 98 | handle_cast({record, Name}, State) -> 99 | {Date, _} = calendar:universal_time(), 100 | handle_cast({record, Name, Date}, State); 101 | handle_cast(R= {record, Name, Date}, State) -> 102 | Key = bucket_key(Name, Date), 103 | case dets:lookup(twoorl_stats, Key) of 104 | [_] -> dets:update_counter(twoorl_stats, Key, {3, 1}); 105 | _ -> dets:insert(twoorl_stats, [{Key, Date, 1}]) 106 | end, 107 | {noreply, State}; 108 | 109 | handle_cast(_Msg, State) -> {noreply, State}. 110 | 111 | handle_info(_Info, State) -> {noreply, State}. 112 | 113 | terminate(_Reason, _State) -> ok. 114 | 115 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 116 | 117 | ensure_started() -> 118 | case whereis(twoorl_stats) of 119 | X when is_pid(X) -> ok; 120 | _ -> twoorl_stats:start() 121 | end. 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/twoorl_sup.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Nick Gerakines, 2008 17 | %% 18 | %% @author Nick Gerakines [http://blog.socklabs.com/] 19 | %% @copyright Nick Gerakines, 2008 20 | %% @doc Provide the supervisor tree structure for the application. 21 | -module(twoorl_sup). 22 | -behaviour(supervisor). 23 | 24 | -export([start_link/1, init/1]). 25 | 26 | start_link(Args) -> 27 | supervisor:start_link({local, ?MODULE}, ?MODULE, Args). 28 | 29 | init(Args) -> 30 | {ok, {{one_for_one, 10, 10}, 31 | [{twoorl_yaws, 32 | {twoorl_server, start_link, [Args]}, 33 | permanent, 20000, worker, [twoorl_server]}]}}. 34 | -------------------------------------------------------------------------------- /src/twoorl_twitter.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(twoorl_twitter). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | send_tweet(Usr, Msg) -> 26 | Res = twitter_client:status_update( 27 | Usr:twitter_username(), 28 | Usr:twitter_password(), 29 | [{"status", Msg:body_raw()}, 30 | {"source", "twoorl"}]), 31 | case Res of 32 | {error, _} -> 33 | ?Warn("error sending tweet ~p ~p", [Msg:id(), Res]), 34 | msg:update([{twitter_status, ?TWITTER_SENT_ERR}], 35 | {id, '=', Msg:id()}); 36 | _ -> 37 | %% twoorl_stats:cast({record, twitter_crosspost}), 38 | msg:update([{twitter_status, ?TWITTER_SENT_OK}], 39 | {id, '=', Msg:id()}) 40 | end. 41 | -------------------------------------------------------------------------------- /src/twoorl_util.erl: -------------------------------------------------------------------------------- 1 | %% This file is part of Twoorl. 2 | %% 3 | %% Twoorl is free software: you can redistribute it and/or modify 4 | %% it under the terms of the GNU General Public License as published by 5 | %% the Free Software Foundation, either version 3 of the License, or 6 | %% (at your option) any later version. 7 | %% 8 | %% Twoorl is distributed in the hope that it will be useful, 9 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | %% GNU General Public License for more details. 12 | %% 13 | %% You should have received a copy of the GNU General Public License 14 | %% along with Twoorl. If not, see . 15 | %% 16 | %% Copyright Yariv Sadan, 2008 17 | %% 18 | %% @author Yariv Sadan [http://yarivsblog.com] 19 | %% @copyright Yariv Sadan, 2008 20 | 21 | -module(twoorl_util). 22 | -compile(export_all). 23 | -include("twoorl.hrl"). 24 | 25 | cookie(Key, Val) -> 26 | yaws_api:setcookie(Key, Val, "/", "Wed 01-01-2020 00:00:00 GMT"). 27 | 28 | gen_key() -> 29 | gen_key(?DEFAULT_KEY_SIZE). 30 | 31 | gen_key(Len) -> 32 | Chars = 33 | {$a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l,$m,$n,$o,$p,$q,$r,$s,$t,$u,$v, 34 | $w,$x,$y,$z, 35 | $A,$B,$C,$D,$E,$F,$G,$H,$I,$J,$K,$L,$M,$N,$O,$P,$Q,$R,$S,$T,$U,$V, 36 | $W,$X,$Y,$Z, 37 | $1,$2,$3,$4,$5,$6,$7,$8,$9,$0}, 38 | Res = lists:map( 39 | fun(_) -> 40 | Idx = crypto:rand_uniform(1, 62), 41 | element(Idx, Chars) 42 | end, lists:seq(1, Len)), 43 | list_to_binary(Res). 44 | 45 | 46 | join(List) -> 47 | join(List, ","). 48 | 49 | join(List, Delim) -> 50 | lists:foldl( 51 | fun(Elem, []) -> 52 | [Elem]; 53 | (Elem, Acc) -> 54 | [Elem, Delim | Acc] 55 | end, [], lists:reverse(List)). 56 | 57 | get_seconds_since({datetime, Val}) -> 58 | get_seconds_since(Val); 59 | get_seconds_since(Val) -> 60 | calendar:datetime_to_gregorian_seconds( 61 | calendar:local_time()) - 62 | calendar:datetime_to_gregorian_seconds(Val). 63 | 64 | get_time_since(DateTime) -> 65 | Diff = 66 | get_seconds_since(DateTime), 67 | {Type, Val} = 68 | if Diff < 60 -> 69 | {seconds_ago, Diff}; 70 | Diff < 3600 -> 71 | {minutes_ago, round(Diff/60)}; 72 | Diff < 86400 -> 73 | {hours_ago, round(Diff/3600)}; 74 | true -> 75 | {days_ago, round(Diff/86400)} 76 | end, 77 | {Type, integer_to_list(Val)}. 78 | 79 | auth(A, Fun) -> 80 | auth(A, Fun, fun() -> {ewr, login} end). 81 | 82 | auth(A, Fun, ElseFun) -> 83 | case get_usr(A) of 84 | undefined -> 85 | ElseFun(); 86 | Usr -> 87 | Fun(Usr) 88 | end. 89 | 90 | get_usr(A) -> 91 | Session = yaws_arg:get_opaque_val(A, session), 92 | if Session =/= undefined -> 93 | Session#session.value; 94 | true -> 95 | undefined 96 | end. 97 | 98 | 99 | htmlize(undefined) -> undefined; 100 | htmlize(List) when is_list(List) -> htmlize_l(List, []). % modified by Michael 101 | 102 | htmlize_l(List) -> htmlize_l(List, []). 103 | htmlize_l([], Acc) -> lists:reverse(Acc); 104 | 105 | htmlize_l([13,10|Tail], Acc) -> htmlize_l(Tail, ["
"|Acc]); 106 | htmlize_l([13|Tail], Acc) -> htmlize_l(Tail, ["
"|Acc]); 107 | htmlize_l([10|Tail], Acc) -> htmlize_l(Tail, ["
"|Acc]); 108 | htmlize_l([$>|Tail], Acc) -> htmlize_l(Tail, [$;,$t,$g,$&|Acc]); 109 | htmlize_l([$<|Tail], Acc) -> htmlize_l(Tail, [$;,$t,$l,$&|Acc]); 110 | htmlize_l([$&,A,$;|Tail], Acc) -> htmlize_l(Tail, [$;,A,$&|Acc]); 111 | htmlize_l([$&,A,B,$;|Tail], Acc) -> htmlize_l(Tail, [$;,B,A,$&|Acc]); 112 | htmlize_l([$&,A,B,C,$;|Tail], Acc) -> htmlize_l(Tail, [$;,C,B,A,$&|Acc]); 113 | htmlize_l([$&,A,B,C,D,$;|Tail], Acc) -> htmlize_l(Tail, [$;,D,C,B,A,$&|Acc]); 114 | htmlize_l([$&,A,B,C,D,E,$;|Tail], Acc) -> 115 | htmlize_l(Tail, [$;,E,D,C,B,A,$&|Acc]); 116 | htmlize_l([$&,A,B,C,D,E,F,$;|Tail], Acc) -> 117 | htmlize_l(Tail, [$;,F,E,D,C,B,A,$&|Acc]); 118 | htmlize_l([$&,A,B,C,D,E,F,G,$;|Tail], Acc) -> 119 | htmlize_l(Tail, [$;,G,F,E,D,C,B,A,$&|Acc]); 120 | htmlize_l([$&,A,B,C,D,E,F,G,H,$;|Tail], Acc) -> 121 | htmlize_l(Tail, [$;,H,G,F,E,D,C,B,A,$&|Acc]); 122 | htmlize_l([$&|Tail], Acc) -> htmlize_l(Tail, [$;,$p,$m,$a,$&|Acc]); 123 | htmlize_l([$"|Tail], Acc) -> htmlize_l(Tail, [$;,$t,$o,$u,$q,$&|Acc]); 124 | htmlize_l([X|Tail], Acc) when integer(X) -> htmlize_l(Tail,[X|Acc]); 125 | htmlize_l([X|Tail], Acc) when binary(X) -> 126 | X2 = htmlize_l(binary_to_list(X)), htmlize_l(Tail, [X2|Acc]); 127 | htmlize_l([X|Tail], Ack) when is_list(X) -> 128 | X2 = htmlize_l(X), 129 | htmlize_l(Tail, [X2|Ack]). 130 | 131 | 132 | log(Module, Line, Level, FormatFun) -> 133 | Func = case Level of 134 | debug -> 135 | % info_msg; 136 | undefined; 137 | info -> 138 | info_msg; 139 | normal -> 140 | info_msg; 141 | error -> 142 | error_msg; 143 | warn -> 144 | warning_msg 145 | end, 146 | if Func == undefined -> 147 | ok; 148 | true -> 149 | {Format, Params} = FormatFun(), 150 | error_logger:Func("~w:~b: "++ Format ++ "~n", 151 | [Module, Line | Params]) 152 | end. 153 | 154 | %% @doc Replace all the Body's substrings matching the RegExp 155 | %% or Matches list using the 156 | %% ReplaceFun function and without exceeding MaxLen characters. 157 | %% 158 | %% ReplaceFun takes one parameter: the substring to 159 | %% be matched. It returns a tuple of the form {Replacement, 160 | %% EffectiveReplacement} or just a replacement string. 161 | %% Replacement is the new substring. EffectiveReplacement (optional) 162 | %% is the "real" value of the replacement, which will be used for 163 | %% calculating the replacement's length and which will be returned in the 164 | %% function's return value. (EffectiveReplacement helps deal with links.) 165 | %% 166 | %% The return value is {Result, Changes}. 167 | %% Result is the new string with the substitutions applied (and whose length 168 | %% doesn't exceed MaxLen). Changes is a list of all {Match, Replacement} pairs 169 | %% where Match is the substring matching RegExp and Replacement is its 170 | %% replacement. 171 | %% 172 | %% @spec replace_matches(Body::string(), 173 | %% RegExp::regexp() | [{integer(), integer()}], 174 | %% ReplaceFun::function(), MaxLen::integer()) -> 175 | %% {NewBody::iolist(), [{string(), string()}]} 176 | replace_matches(Body, RegExp, ReplaceFun, MaxLen) when is_tuple(RegExp) -> 177 | {match, Matches} = regexp:matches(Body, RegExp), 178 | replace_matches(Body, Matches, ReplaceFun, MaxLen); 179 | replace_matches(Body, Matches, ReplaceFun, MaxLen) -> 180 | if Matches == [] -> 181 | {lists:sublist(Body, MaxLen), []}; 182 | true -> 183 | replace_matches1(Body, Matches, ReplaceFun, MaxLen) 184 | end. 185 | replace_matches1(Body, Matches, ReplaceFun, MaxLen) -> 186 | {CurIdx1, Acc, RemChars3, MatchAcc2, LenDiffAcc2} = 187 | lists:foldl( 188 | fun({_Begin, _MatchLength}, 189 | {CurIdx, _Acc, _RemChars, _MatchAcc, LenDiffAcc} = Res) 190 | when CurIdx + LenDiffAcc > MaxLen-> 191 | %% ignore the match if we passed MaxLen chars 192 | Res; 193 | ({Begin, MatchLength}, 194 | {CurIdx, Acc, RemChars, MatchAcc, LenDiffAcc}) -> 195 | PrefixLen = Begin - CurIdx, 196 | {Prefix, RemChars1} = lists:split(PrefixLen, RemChars), 197 | {Match, RemChars2} = lists:split(MatchLength, RemChars1), 198 | {Replacement1, EffectiveReplacement1, 199 | EffectiveReplacementLen} = 200 | case ReplaceFun(Match) of 201 | {Replacement2, EffectiveReplacement2} -> 202 | {Replacement2, EffectiveReplacement2, 203 | length(EffectiveReplacement2)}; 204 | Replacement2 -> 205 | {Replacement2, Replacement2, 206 | length(Replacement2)} 207 | end, 208 | 209 | LenDiff = EffectiveReplacementLen - MatchLength, 210 | OverFlow = (CurIdx + PrefixLen + EffectiveReplacementLen) - 211 | (MaxLen - LenDiffAcc), 212 | 213 | %% if we detect an overflow, we discard the match 214 | {Acc1, LenDiffAcc1} 215 | = if OverFlow > 0 -> 216 | 217 | %% keep the remaining prefix 218 | Rem = (MaxLen - LenDiffAcc) - 219 | (CurIdx - 1), 220 | Prefix1 = 221 | if Rem > PrefixLen -> 222 | Prefix; 223 | Rem < 1 -> 224 | []; 225 | true -> 226 | lists:sublist(Prefix, Rem) 227 | end, 228 | {Prefix1, LenDiffAcc - MatchLength}; 229 | true -> 230 | {[Prefix, Replacement1], LenDiffAcc + LenDiff} 231 | end, 232 | {CurIdx + PrefixLen + MatchLength, [Acc1 | Acc], RemChars2, 233 | [{Match, EffectiveReplacement1} | MatchAcc], LenDiffAcc1} 234 | end, {1, [], Body, [], 0}, Matches), 235 | RemCharsLength = MaxLen - (CurIdx1 - 1) - LenDiffAcc2, 236 | RemChars4 = if RemCharsLength > 0 -> 237 | lists:sublist(RemChars3, RemCharsLength); 238 | true -> 239 | [] 240 | end, 241 | {[lists:reverse(Acc), RemChars4], MatchAcc2}. 242 | 243 | 244 | get_tinyurl("http://tinyurl.com/" ++ Rest = Url) when length(Rest) < 7 -> 245 | get_tinyurl1(Url); 246 | get_tinyurl(Url) when length(Url) < 26 -> get_tinyurl1(Url); 247 | get_tinyurl(Url) -> 248 | TinyApi = "http://tinyurl.com/api-create.php?url=" ++ Url, 249 | case http:request(TinyApi) of 250 | {ok, {{_Protocol, 200, _}, _Headers, Body1}} -> 251 | get_tinyurl1(Body1); 252 | Res -> 253 | ?Error("tinyurl error: ~p", [Res]), 254 | Url 255 | end. 256 | 257 | get_tinyurl1(Body) -> 258 | {["", Body, ""], Body}. 259 | 260 | 261 | 262 | get_session_key(A) -> 263 | yaws_arg:get_opaque_val(A, key). 264 | 265 | update_session(A, Usr) -> 266 | Key = twoorl_util:get_session_key(A), 267 | update_session(A, Usr, Key). 268 | 269 | update_session(A, Usr, Key) -> 270 | NewSession = #session{key=Key, 271 | value=Usr}, 272 | mnesia:dirty_write(NewSession), 273 | Opaque1 = 274 | lists:map(fun({session, _}) -> {session, NewSession}; 275 | (Other) -> Other 276 | end, yaws_arg:opaque(A)), 277 | yaws_arg:opaque(A, Opaque1). 278 | 279 | 280 | gravatar_icon(GravatarId) -> 281 | [<<"\"gravatar\">, 284 | GravatarId, <<"\"/>">>]. 285 | 286 | gravatar_id(Email) -> 287 | digest2str(erlang:md5(Email)). 288 | 289 | digest2str(Digest) -> 290 | [[nibble2hex(X bsr 4), nibble2hex(X band 15)] || 291 | X <- binary_to_list(Digest)]. 292 | 293 | -define(IN(X,Min,Max), X >= Min, X =< Max). 294 | 295 | 296 | nibble2hex(X) when ?IN(X, 0, 9) -> X + $0; 297 | nibble2hex(X) when ?IN(X, 10, 15) -> X - 10 + $a. 298 | 299 | 300 | iolist_fun(Rec) -> 301 | fun(Field) -> 302 | erlydb_base:field_to_iolist(Rec:Field()) 303 | end. 304 | 305 | iolist_fun(Rec, Fun) -> 306 | fun(Field) -> 307 | case Fun(Field) of 308 | default -> 309 | erlydb_base:field_to_iolist(Rec:Field()); 310 | Other -> 311 | Other 312 | end 313 | end. 314 | 315 | 316 | 317 | %% Used for RSS formatting 318 | %% Reference: http://cyber.law.harvard.edu/rss/rss.html 319 | format_datetime({Date = {Year, Month, Day}, {Hour, Minute, Second}}) -> 320 | Daynum = calendar:day_of_the_week(Date), 321 | [day(Daynum), $,, 32, itos(Day), 32, mon(Month), 32, 322 | itos(Year), 32, itos(Hour, 2), $:, itos(Minute, 2), $:, itos(Second, 2), 323 | <<" GMT">>]. 324 | 325 | itos(N) -> 326 | integer_to_list(N). 327 | itos(N, Length) -> 328 | Str = itos(N), 329 | Diff = Length - length(Str), 330 | [lists:duplicate(Diff, $0), Str]. 331 | 332 | day(N) -> 333 | case N of 334 | 1 -> <<"Mon">>; 335 | 2 -> <<"Tue">>; 336 | 3 -> <<"Wed">>; 337 | 4 -> <<"Thu">>; 338 | 5 -> <<"Fri">>; 339 | 6 -> <<"Sat">>; 340 | 7 -> <<"Sun">> 341 | end. 342 | 343 | mon(N) -> 344 | case N of 345 | 1 -> <<"Jan">>; 346 | 2 -> <<"Feb">>; 347 | 3 -> <<"Mar">>; 348 | 4 -> <<"Apr">>; 349 | 5 -> <<"May">>; 350 | 6 -> <<"Jun">>; 351 | 7 -> <<"Jul">>; 352 | 8 -> <<"Aug">>; 353 | 9 -> <<"Sep">>; 354 | 10 -> <<"Oct">>; 355 | 11 -> <<"Nov">>; 356 | 12 -> <<"Dec">> 357 | end. 358 | 359 | 360 | get_feed_link(Url, Format) -> 361 | erlyweb_html:a([Url], Format, [{"class", "feed_link"}]). 362 | 363 | with_bundle(A, Data) -> 364 | {data, {get_bundle(A), Data}}. 365 | 366 | %% codes taken from http://www.loc.gov/standards/iso639-2/php/code_list.php 367 | bundles() -> 368 | [{<<"eng">>, <<"English">>, twoorl_eng}, 369 | {<<"spa">>, <<"Español">>, twoorl_spa}, 370 | {<<"dk">>, <<"Dansk">>, twoorl_dk}, 371 | {<<"deu">>, <<"Deutsch">>, twoorl_deu}, 372 | {<<"fra">>, <<"Français">>, twoorl_fra}, 373 | {<<"gre">>, <<"Greek">>, twoorl_greek}, 374 | {<<"ita">>, <<"Italiano">>, twoorl_ita}, 375 | {<<"kor">>, <<"한국어">>, twoorl_kor}, 376 | {<<"pol">>, <<"Polski">>, twoorl_pol}, 377 | {<<"por">>, <<"Português Brasileiro">>, twoorl_por_br}, 378 | {<<"ru">>, <<"Русский">>, twoorl_rus}, 379 | {<<"sv">>, <<"Svenska">>, twoorl_sv}, 380 | {<<"ch">>, <<"简体中文">>, twoorl_zh_cn}]. 381 | 382 | get_bundle(A) -> 383 | Module1 = 384 | case erlyweb_util:get_cookie("lang", A) of 385 | undefined -> 386 | twoorl_eng; 387 | [] -> 388 | twoorl_eng; 389 | Lang -> 390 | Lang1 = list_to_binary(Lang), 391 | case lists:keysearch(Lang1, 1, bundles()) of 392 | false -> 393 | ?Warn("undefined language: ~p ~p", 394 | [get_usr(A), Lang1]), 395 | twoorl_eng; 396 | {value, {_, _, Module}} -> Module 397 | end 398 | end, 399 | fun(StrId) -> 400 | %% Some values may not have been translated from English. 401 | %% We catch such exceptions and fall back on the english 402 | %% translation. 403 | case catch Module1:bundle(StrId) of 404 | {'EXIT', Err} -> 405 | case Module1 of 406 | twoorl_eng -> 407 | %% this is not supposed to happen 408 | exit(Err); 409 | _ -> 410 | twoorl_eng:bundle(StrId) 411 | end; 412 | Other -> 413 | Other 414 | end 415 | end. 416 | 417 | i18n(A, Val) -> 418 | F = get_bundle(A), F(Val). 419 | 420 | 421 | delete_sessions() -> 422 | Sessions = mnesia:dirty_match_object(#session{_ = '_'}), 423 | lists:foreach(fun mnesia:dirty_delete_object/1, Sessions). 424 | -------------------------------------------------------------------------------- /twoorl.sql: -------------------------------------------------------------------------------- 1 | drop table if exists usr; 2 | create table usr ( 3 | id integer unsigned auto_increment primary key, 4 | username varchar(30) not null, 5 | password char(20) not null, 6 | email varchar(50) not null, 7 | num_msgs integer unsigned not null default 0, 8 | num_replies integer unsigned not null default 0, 9 | created_on timestamp, 10 | unique(username, email) 11 | ) engine=innodb; 12 | 13 | drop table if exists following; 14 | create table following ( 15 | usr_id1 integer unsigned not null, 16 | usr_id2 integer unsigned not null, 17 | 18 | # denormalized usernames to reduce lookups 19 | usr_username1 varchar(30) not null, 20 | usr_username2 varchar(30) not null, 21 | 22 | created_on timestamp, 23 | primary key(usr_id1, usr_id2) 24 | ) engine=innodb; 25 | 26 | 27 | drop table if exists msg; 28 | create table msg ( 29 | id integer unsigned auto_increment primary key, 30 | usr_id integer unsigned not null, 31 | 32 | # denormalized username 33 | usr_username varchar(30) not null, 34 | 35 | # raw data entered by the user 36 | body_raw varchar(140) not null, 37 | 38 | # with pre-escaped html entities and links 39 | body varchar(140) not null, 40 | 41 | created_on timestamp, 42 | index(usr_id, created_on) 43 | ) engine=innodb; 44 | 45 | drop table if exists reply; 46 | create table reply ( 47 | usr_id integer unsigned not null, 48 | msg_id integer unsigned not null, 49 | created_on timestamp, 50 | primary key(usr_id, msg_id), 51 | index(created_on) 52 | ) engine=innodb; 53 | -------------------------------------------------------------------------------- /www/static/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yariv/twoorl/77b6bea2e29283d09a95d8db131b916e16d2960b/www/static/bg.gif -------------------------------------------------------------------------------- /www/static/bg1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yariv/twoorl/77b6bea2e29283d09a95d8db131b916e16d2960b/www/static/bg1.jpg -------------------------------------------------------------------------------- /www/static/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: helvetica, verdana, sans-serif; 3 | color: #353535; 4 | margin:10px 0px; padding:0px; 5 | text-align:center; 6 | } 7 | form input { 8 | margin-bottom: 5px; 9 | } 10 | 11 | .section1, .paging_content, #txt_form { 12 | margin-bottom: 10px; 13 | } 14 | 15 | #content { 16 | width:550px; 17 | margin:0px auto; 18 | text-align:left; 19 | padding:15px; 20 | background-color: #fff; 21 | } 22 | 23 | #top { 24 | width: 100%; 25 | background-color: #eee; 26 | padding: 5px; 27 | } 28 | 29 | #logo a{ 30 | font-size: 1.3em; 31 | } 32 | 33 | #header { 34 | text-align: right; 35 | font-size: 0.8em; 36 | vertical-align: middle; 37 | } 38 | 39 | .header1 { 40 | padding-bottom: 10px; 41 | border-bottom: #999 solid 1px; 42 | } 43 | 44 | .t1 {font-size: 1.5em;} 45 | 46 | .t2, .t3 { 47 | font-size: 1.5em; 48 | } 49 | 50 | .t2, .section { 51 | border-bottom: #999 solid 1px; 52 | padding-bottom: 20px; 53 | } 54 | 55 | #txt_table { 56 | width: 500px; 57 | margin: 0px auto; 58 | } 59 | 60 | #txt { 61 | width: 500px; 62 | height: 50px; 63 | } 64 | 65 | #chars { 66 | color: #ddd; 67 | font-size: 1.3em; 68 | } 69 | 70 | .err_msg { 71 | color: #f00; 72 | } 73 | 74 | .confirm_msg { 75 | color: #0d0; 76 | } 77 | 78 | .msg { 79 | border-bottom: #999 solid 1px; 80 | padding: 5px 3px; 81 | } 82 | .usrlink { 83 | font-size: 1.1em; 84 | } 85 | 86 | a { 87 | text-decoration: none; 88 | color: #00c; 89 | } 90 | 91 | a:hover { 92 | text-decoration: underline; 93 | } 94 | a:visited { 95 | color: #00c; 96 | } 97 | 98 | .bigmsg .msgbody { 99 | font-size: 3em; 100 | } 101 | 102 | .time { 103 | font-size: 0.7em; 104 | color: #666; 105 | white-space: nowrap; 106 | } 107 | 108 | .following_links { 109 | padding-left: 10px; 110 | vertical-align: bottom; 111 | } 112 | 113 | #navbar { 114 | margin: auto; 115 | } 116 | 117 | .paging_links { 118 | border-collapse: collapse; 119 | } 120 | .paging_link { 121 | padding: 1px 3px; 122 | border: #aaa solid 1px; 123 | } 124 | 125 | #footer { 126 | margin: 20px auto 0px auto; 127 | font-size: 0.7em; 128 | } 129 | 130 | .twoorl { 131 | padding: 50px 50px; 132 | } 133 | 134 | .twoorl_body { 135 | font-size: 3em; 136 | } 137 | 138 | fieldset { 139 | margin: 0px; 140 | padding: 0px; 141 | border: 0px; 142 | } 143 | 144 | #twitter_msg { 145 | font-size: 0.8em; 146 | text-align: left; 147 | color: #999; 148 | } 149 | 150 | .user_icon { 151 | text-align: center; 152 | padding: 3px; 153 | } 154 | 155 | #language_select_box { 156 | margin: 10px 0px 10px 0px; 157 | text-align: center 158 | } 159 | 160 | .gravatar { border:0px; } -------------------------------------------------------------------------------- /www/static/twoorl.js: -------------------------------------------------------------------------------- 1 | var maxChars = 140; 2 | 3 | $(function() { 4 | $("#input1").focus(); 5 | $("#txt").focus().keypress(function(e) { 6 | if (e.which == 13) { //enter 7 | send(); 8 | return false; 9 | } 10 | var length = val().length+1; 11 | $("#chars").text("" + (maxChars - length)); 12 | 13 | }); 14 | $("#language_select").change(function(e) { 15 | var select = $("#language_select")[0]; 16 | var lang = select.options[select.selectedIndex].value; 17 | document.cookie = 18 | 'lang=' + lang + '; expires=Wed, 1 Jan 2020 00:00:00 UTC; path=/'; 19 | location.href = location.href; 20 | }); 21 | 22 | }); 23 | 24 | function val() { 25 | return $("#txt")[0].value; 26 | } 27 | 28 | function send() { 29 | var msg = val(); 30 | if (msg.length > 0) { 31 | $("#txt")[0].value = ""; 32 | $("#chars").text("" + maxChars); 33 | $.post("/api/send", {"msg": msg, "get_html": "true"}, 34 | function(html) { 35 | $("#placeholder").prepend(html); 36 | $("#txt").focus(); 37 | }); 38 | } 39 | } 40 | 41 | function follow(username, val) { 42 | $.post("/api/follow", 43 | {"username": username, 44 | "value": val}, 45 | function(res) { 46 | if (val == "1") { 47 | $("#follow").hide(); 48 | $("#unfollow").show(); 49 | } else { 50 | $("#follow").show(); 51 | $("#unfollow").hide(); 52 | } 53 | }); 54 | 55 | } 56 | 57 | function toggle_twitter(input) { 58 | $.post("/api/toggle_twitter", 59 | {"value": input.value == "on"}, 60 | function(res) { 61 | if (res != "ok") { 62 | alert("Toggle Twitter failed"); 63 | } 64 | }); 65 | } 66 | 67 | --------------------------------------------------------------------------------