tinycm view index.cgi @ rev 64

Tiny edits
author Paul Issott <paul@slitaz.org>
date Thu Feb 06 19:35:03 2014 +0000 (2014-02-06)
parents 1293a00e2a72
children 007a950f9808
line source
1 #!/bin/sh
2 #
3 # TinyCM - Small, fast and elegant CGI/SHell Content Manager
4 #
5 # Copyright (C) 2012-2014 SliTaz GNU/Linux - BSD License
6 #
7 . /usr/lib/slitaz/httphelper
9 # Let's have a peer site config file with a .cgi extension so content
10 # is secure even if left in a web server directory.
11 . ./config.cgi
13 tiny="$PWD"
14 content="content"
15 wiki="$content/wiki"
16 index="index"
17 cache="cache"
18 plugins="plugins"
19 tmp="/tmp/tinycm"
20 sessions="$tmp/sessions"
21 script="$SCRIPT_NAME"
22 activity="$cache/log/activity.log"
24 # Content negotiation for Gettext
25 IFS=","
26 for lang in $HTTP_ACCEPT_LANGUAGE
27 do
28 lang=${lang%;*} lang=${lang# } lang=${lang%-*}
29 case "$lang" in
30 en) lang="C" && break ;;
31 fr) lang="fr_FR" && break ;;
32 pt) lang="pt_BR" && break ;;
33 ru) lang="ru_RU" && break ;;
34 esac
35 done
36 unset IFS
37 export LANG=$lang LC_ALL=$lang
39 # Internationalization
40 . /usr/bin/gettext.sh
41 TEXTDOMAIN='tinycm'
42 export TEXTDOMAIN
44 #
45 # Functions
46 #
48 # Used by edit to display language name and the language box. This is
49 # for CM content not gettext support.
50 get_lang() {
51 dlang=$(echo $d | cut -d "/" -f 1)
52 doc=${d#$dlang/}
53 echo '<div id="lang">'
54 for l in $LANGUAGES
55 do
56 case $dlang in
57 en) i18n="English" ;;
58 fr) i18n="Français" ;;
59 pt) i18n="Português" ;;
60 ru) i18n="Русский" ;;
61 *) i18n="*" ;;
62 esac
63 echo "<a href='?d=$l/$doc'>$l</a>"
64 done
65 echo '</div>'
66 }
68 # HTML 5 header.
69 html_header() {
70 if [ -f "$tiny/lib/header.html" ]; then
71 cat $tiny/lib/header.html | sed -e s!'%TITLE%'!"$TITLE - $d"!g
72 else
73 cat << EOT
74 <!DOCTYPE html>
75 <html xmlns="http://www.w3.org/1999/xhtml">
76 <head>
77 <title>$TITLE</title>
78 <meta charset="utf-8" />
79 <style type="text/css">body { margin: 40px 120px; }</style>
80 </head>
81 <body>
82 <!-- Content -->
83 <div id="content">
84 EOT
85 fi
86 }
88 # HTML 5 footer.
89 html_footer() {
90 if [ -f "$tiny/lib/footer.html" ]; then
91 cat $tiny/lib/footer.html
92 else
93 cat << EOT
95 <!-- End content -->
96 </div>
98 <div id="footer">
99 I &hearts; <a href="http://tinycm.slitaz.org/">TinyCM</a>
100 </div>
102 </body>
103 </html>
104 EOT
105 fi
106 }
108 # Default index if missing
109 default_index() {
110 mkdir -p "$wiki"
111 cat > $wiki/$index.txt << EOT
112 ==== Welcome ====
114 <p>
115 This is the default index page of your TinyCM, you can login then start to
116 edit and adding some content. You can read the help about text formating
117 and functions: [Help page|en/help]
118 </p>
120 EOT
121 }
123 # Log main activity.
124 log_activity() {
125 [ -d "$cache/log" ] || mkdir -p ${cache}/log
126 #gravatar="$(get_gravatar $MAIL 24)"
127 grep ^[A-Z] | \
128 sed s"#^[A-Z]\([^']*\)#$user|$(date '+%Y-%m-%d')|\0#" \
129 >> $cache/log/activity.log
130 }
132 # Log documents activity.
133 log() {
134 grep ^[A-Z] | \
135 sed s"#^[A-Z]\([^']*\)#$(date '+%Y-%m-%d %H:%M') : \0#" \
136 >> $cache/$d/activity.log
137 }
139 # Check if user is auth
140 check_auth() {
141 auth="$(COOKIE auth)"
142 user="$(echo $auth | cut -d ":" -f 1)"
143 md5cookie="$(echo $auth | cut -d ":" -f 2)"
144 [ -f "$sessions/$user" ] && md5session="$(cat $sessions/$user)"
145 if [ "$md5cookie" == "$md5session" ] && [ "$auth" ]; then
146 . $PEOPLE/$user/account.conf
147 return 0
148 else
149 return 1
150 fi
151 }
153 # Check if user is admin
154 admin_user() {
155 fgrep -q 'ADMIN_USER="yes"' ${PEOPLE}/${user}/account.conf
156 }
158 # Authenticated or not
159 user_box() {
160 if check_auth; then
161 cat << EOT
163 <div id="user">
164 <a href="$script?user=$user">$(get_gravatar $MAIL 20)</a>
165 <a href="$script?logout">Logout</a>
166 </div>
168 EOT
169 else
170 cat << EOT
172 <div id="user">
173 <a href="$script?login"><img src="images/avatar.png" alt="[ User ]" /></a>
174 <a href="$script?login">Login</a>
175 </div>
177 EOT
178 fi
179 cat << EOT
180 <!--
181 <div id="search">
182 <form method="get" action="$script">
183 <input type="text" name="search" placeholder="$(gettext "Search")" />
184 </form>
185 </div>
186 -->
187 EOT
188 }
190 # Link for online signup if enabled.
191 online_signup() {
192 if [ "$ONLINE_SIGNUP" == "yes" ]; then
193 echo -n "<p><a href='$script?signup'>"
194 gettext "Create a new account"
195 echo '</a></p>'
196 fi
197 }
199 # Login page
200 login_page() {
201 cat << EOT
202 <h2>$(gettext "Login")</h2>
204 <div id="account-info">
205 $(gettext "No account yet or trouble with your account? Please send
206 a request to $ADMIN_MAIL with your real name, user name, mail and password.")
207 $(online_signup)
208 </div>
210 <div id="login">
211 <form method="post" action="$script">
212 <input type="text" name="auth" placeholder="$(gettext "User name")" />
213 <input type="password" name="pass" placeholder="$(gettext "Password")" />
214 <div>
215 <input type="submit" value="Login" /> $error
216 </div>
217 </form>
218 </div>
220 <div style="clear: both;"></div>
221 EOT
222 }
224 # Signup page
225 signup_page() {
226 cat << EOT
228 <div id="signup">
229 <form method="post" name="signup" action="$script" onsubmit="return checkSignup();">
230 <input type="hidden" name="signup" value="new" />
231 <input type="text" name="name" placeholder="$(gettext "Real name")" />
232 <input type="text" name="user" placeholder="$(gettext "User name")" />
233 <input type="text" name="mail" placeholder="$(gettext "Email")" />
234 <input type="password" name="pass" placeholder="$(gettext "Password")" />
235 <div>
236 <input type="submit" value="$(gettext "Create new account")" />
237 </div>
238 </form>
239 </div>
241 EOT
242 }
244 # Create a new user in AUTH_FILE and PEOPLE
245 new_user_config() {
246 if [ ! -f "$AUTH_FILE" ];then
247 touch $AUTH_FILE
248 chmod 0600 $AUTH_FILE
249 fi
250 key=$(echo -n "$user:$mail:$pass" | md5sum | awk '{print $1}')
251 echo "$user:$pass" >> $AUTH_FILE
252 mkdir -p $PEOPLE/$user/
253 cat > $PEOPLE/$user/account.conf << EOT
254 # SliTaz user configuration
255 #
257 NAME="$name"
258 USER="$user"
259 MAIL="$mail"
260 KEY="$key"
262 EOT
263 chmod 0600 $PEOPLE/$user/account.conf
264 # First created user is admin
265 if [ $(ls ${PEOPLE} | wc -l) == "1" ]; then
266 echo 'ADMIN_USER="yes"' >> $PEOPLE/$user/account.conf
267 fi
268 }
270 # Display user public profile.
271 public_people() {
272 echo "</pre>"
273 # Display personal user profile
274 if [ -f "$PEOPLE/$USER/profile.txt" ]; then
275 cat $PEOPLE/$USER/profile.txt | wiki_parser
276 fi
277 }
279 # Display authenticated user profile. TODO: change password
280 auth_people() {
281 cat << EOT
282 Email : $MAIL
283 Secure key : $KEY
284 </pre>
285 EOT
286 # Each user can have personal profile page
287 if [ -f "$PEOPLE/$USER/profile.txt" ]; then
288 cat $PEOPLE/$USER/profile.txt | wiki_parser
289 cat << EOT
290 <div id="tools">
291 <a href="$script?edit=profile">$(gettext "Edit profile")</a>
292 <a href="$script?dashboard">Dashboard</a>
293 </div>
294 EOT
295 else
296 cat << EOT
297 <div id="tools">
298 <a href="$script?edit=profile">$(gettext "Create a profile page")</a>
299 <a href="$script?dashboard">Dashboard</a>
300 </div>
301 EOT
302 fi
303 }
305 # The CM style parser. Just a title, simple text formatting and internal
306 # links, as well as images and use HTML for other stuff. Keep it fast!
307 # To make TinyCM as easy as possible we have a small HTML editor/helper
308 # written in Javascript
309 wiki_parser() {
310 doc="[0-9a-zA-Z\.\#/~\_%=\?\&,\+\:@;!\(\)\*\$'\-]*"
311 sed \
312 -e s"#====\([^']*\)====#<h2>\1</h2>#"g \
313 -e s"#===\([^']*\)===#<h3>\1</h3>#"g \
314 -e s"#==\([^']*\)==#<h4>\1</h4>#"g \
315 -e s"#\*\*\([^']*\)\*\*#<b>\1</b>#"g \
316 -e s"#''\([^']*\)''#<em>\1</em>#"g \
317 -e s"#__\([^']*\)__#<u>\1</u>#"g \
318 -e s"#\[\([^]]*\)|\($doc\)\]#<a href='$script?d=\2'>\1</a>#"g \
319 -e s"#\[\([^]]*\)!\($doc\)\]#<a href='\2'>\1</a>#"g \
320 -e s"#\[\(http://*[^]]*.png\)\]#<img src='\1' />#"g \
321 -e s"#\[\([^]]*.png\)\]#<img src='content/cloud/\1' />#"g
322 }
324 link_user() {
325 echo "<a href='$(basename $script)?user=$user'>$user</a>"
326 }
328 # Save a document. Do we need more than 1 backup and diff ?
329 save_document() {
330 mkdir -p $cache/$d $(dirname $wiki/$d)
331 # May be a new page.
332 if [ ! -f "$wiki/$d.txt" ]; then
333 new=0
334 touch $wiki/$d.txt
335 fi
336 cp $wiki/$d.txt $cache/$d/last.bak
337 sed "s/$(echo -en '\r') /\n/g" > $wiki/$d.txt << EOT
338 $(GET content)
339 EOT
340 diff $cache/$d/last.bak $wiki/$d.txt > $cache/$d/last.diff
341 # Log
342 if [ "$new" ]; then
343 echo "Page created by: $(link_user)" | log
344 echo "New document: <a href='$script?d=$d'>$d</a>" | log_activity
345 if [ "$HG" == "yes" ]; then
346 cd $content && hg -q add
347 hg commit -q -u "$NAME <$MAIL>" -m "Created new document: $d"
348 cd $tiny
349 fi
350 else
351 # Here we may clean log: cat && tail -n 40
352 echo "Page edited by: $(link_user)" | log
353 if [ "$HG" == "yes" ]; then
354 cd $content && hg commit -q -u "$NAME <$MAIL>" \
355 -m "Edited document: $d"
356 cd $tiny
357 fi
358 fi
359 }
361 # Save a user profile.
362 save_profile() {
363 path="$PEOPLE/$user"
364 cp -f ${path}/${d}.txt ${path}/${d}.bak
365 sed "s/$(echo -en '\r') /\n/g" > ${path}/${d}.txt << EOT
366 $(GET content)
367 EOT
368 }
370 # CM tools (edit, diff, etc) for auth users
371 wiki_tools() {
372 if check_auth; then
373 cat << EOT
374 <div id="tools">
375 <a href="$script?edit=$d">$(gettext "Edit document")</a>
376 <a href="$script?log=$d">$(gettext "File log")</a>
377 <a href="$script?diff=$d">$(gettext "Last diff")</a>
378 $PLUGINS_TOOLS
379 EOT
380 [ "$HG" == "yes" ] && echo "<a href='$script?hg'>Hg Log</a>"
381 echo "</div>"
382 fi
383 }
385 # Built-in tools such as log/ls and PLUGINS_TOOLS
386 tiny_tools() {
387 if check_auth; then
388 cat << EOT
389 <div id='tools'>
390 <a href='$script?log'>Activity log</a>
391 <a href='$script?ls'>Pages list</a>
392 $PLUGINS_TOOLS
393 </div>
394 EOT
395 fi
396 }
398 # Get and display Gravatar image: get_gravatar email size
399 # Link to profile: <a href="http://www.gravatar.com/$md5">...</a>
400 get_gravatar() {
401 email=$1
402 size=$2
403 [ "$size" ] || size=48
404 url="http://www.gravatar.com/avatar"
405 md5=$(md5crypt $email)
406 echo "<img src='$url/$md5?d=identicon&s=$size' alt='&lowast;' />"
407 }
409 # List hg logs
410 hg_log() {
411 cd $content
412 cat << EOT
413 <table>
414 <thead>
415 <td>$(gettext "User")</td>
416 <td>$(gettext "Description")</td>
417 <td>$(gettext "Revision")</td>
418 </thead>
419 EOT
420 hg log --template "<tr><td>{author}</td><td>{desc}</td><td>{rev}</td></tr>\n"
421 echo '</table>'
422 }
424 #
425 # POST actions
426 #
428 case " $(POST) " in
429 *\ auth\ *)
430 # Authenticate user. Create a session file in $sessions to be used
431 # by check_auth. We have the user login name and a peer session
432 # md5 string in the COOKIE.
433 user="$(POST auth)"
434 pass="$(md5crypt "$(POST pass)")"
435 valid=$(fgrep "${user}:" $AUTH_FILE | cut -d ":" -f 2)
436 if [ "$pass" == "$valid" ] && [ "$pass" != "" ]; then
437 md5session=$(echo -n "$$:$user:$pass:$$" | md5sum | awk '{print $1}')
438 [ -d $sessions ] || mkdir -p $sessions
439 date '+%Y-%m-%d' > ${PEOPLE}/${user}/last
440 echo "$md5session" > $sessions/$user
441 header "Location: $script" \
442 "Set-Cookie: auth=$user:$md5session; HttpOnly"
443 else
444 header "Location: $script?login&error"
445 fi ;;
446 *\ signup\ *)
447 # POST action for signup
448 name="$(POST name)"
449 user="$(POST user)"
450 mail="$(POST mail)"
451 pass="$(md5crypt "$(POST pass)")"
452 if ! grep "^${user}:" $AUTH_FILE; then
453 new_user_config
454 header "Location: $script?login"
455 else
456 header
457 html_header
458 user_box
459 echo "<h2>$(gettext 'User already exists:') $user</h2>"
460 html_footer
461 fi ;;
462 esac
464 #
465 # Plugins
466 #
467 for p in $(ls -1 $plugins)
468 do
469 [ -f "$plugins/$p/$p.conf" ] && . $plugins/$p/$p.conf
470 [ -x "$plugins/$p/$p.cgi" ] && . $plugins/$p/$p.cgi
471 done
473 #
474 # GET actions
475 #
477 case " $(GET) " in
478 *\ edit\ *)
479 d="$(GET edit)"
480 header
481 html_header
482 user_box
483 get_lang
484 wiki_tools
485 if check_auth; then
486 if [ "$doc" == "profile" ]; then
487 wiki="$PEOPLE/$user"
488 fi
489 cat << EOT
490 <h2>$(gettext "Edit $doc [ $i18n ]")</h2>
492 <div id="edit">
494 <form method="get" action="$script" name="editor">
495 <input type="hidden" name="save" value="$d" />
496 <textarea name="content">$(cat "$wiki/$d.txt")</textarea>
497 <input type="submit" value="$(gettext "Save document")" />
498 $(gettext "Code Helper:")
499 $(cat lib/jseditor.html)
500 </form>
502 </div>
503 EOT
504 else
505 gettext "You must be logged in to edit pages"
506 fi
507 html_footer ;;
509 *\ save\ *)
510 d="$(GET save)"
511 if check_auth; then
512 # User profile
513 if [ "$d" == "profile" ]; then
514 save_profile
515 header "Location: $script?user=$user"
516 else
517 save_document
518 fi
519 fi
520 header "Location: $script?d=$d" ;;
522 *\ log\ *)
523 d="$(GET log)"
524 header
525 html_header
526 user_box
527 # Main activity
528 if [ "$d" == "log" ]; then
529 tiny_tools
530 echo "<h2>$(gettext "Activity log")</h2>"
531 echo '<pre>'
532 if [ -f "$cache/log/activity.log" ]; then
533 IFS="|"
534 tac $cache/log/activity.log | while read USER DATE LOG
535 do
536 . ${PEOPLE}/${USER}/account.conf
537 cat << EOT
538 <a href='$script?user=$USER'>$(get_gravatar $MAIL 24)</a>\
539 <span class='date'>$DATE -</span> $LOG
540 EOT
541 done
542 unset IFS
543 else
544 gettext "No activity log yet"; echo
545 fi
546 echo '</pre>'
547 html_footer && exit 0
548 fi
549 # Document activity
550 get_lang
551 wiki_tools
552 echo "<h2>$(gettext "Activity for:") <a href='$script?d=$d'>$d</a></h2>"
553 echo '<pre>'
554 if [ -f "$cache/$d/activity.log" ]; then
555 tac $cache/$d/activity.log
556 else
557 gettext "No log for: $d"; echo
558 fi
559 echo '</pre>'
560 html_footer ;;
562 *\ ls\ *)
563 d="Document list"
564 header
565 html_header
566 user_box
567 tiny_tools
568 [ ! check_auth ] && auth=0
569 echo "<h2>$(gettext "Pages list")</h2>"
570 echo '<pre>'
571 cd ${wiki}
572 for d in $(find . -type f | sed s'/.\///')
573 do
574 echo "<a href='$script?d=${d%.txt}'>${d%.txt}</a>"
575 [ "$auth" ] && cat << EOT
576 : <a href="$script?rm=$d">$(gettext "Remove")</a> || \
577 <a href="$script?edit=$d">$(gettext "Edit")</a>
578 EOT
579 done && unset auth
580 echo '</pre>'
581 html_footer ;;
583 *\ rm\ *)
584 [ ! check_auth ] && exit 1
585 d="$(GET rm)"
586 rm ${wiki}/"${d}"
587 rm -rf ${cache}/"${d%.txt}"
588 header "Location: $script?ls" ;;
590 *\ diff\ *)
591 d="$(GET diff)"
592 date="last"
593 header
594 html_header
595 user_box
596 get_lang
597 wiki_tools
598 echo "<h2>$(gettext "Diff for:") <a href='$script?d=$d'>$d</a></h2>"
599 echo '<pre>'
600 if [ -f "$cache/$d/$date.diff" ]; then
601 cat $cache/$d/$date.diff | sed \
602 -e 's|&|\&amp;|g' -e 's|<|\&lt;|g' -e 's|>|\&gt;|g' \
603 -e s"#^-\([^']*\).#<span style='color: red;'>\0</span>#"g \
604 -e s"#^+\([^']*\).#<span style='color: green;'>\0</span>#"g \
605 -e s"#@@\([^']*\)@@#<span style='color: blue;'>@@\1@@</span>#"g
606 else
607 gettext "No diff for:"; echo " $d"
608 fi
609 echo '</pre>'
610 html_footer ;;
612 *\ login\ *)
613 # The login page
614 d="Login"
615 [ "$(GET error)" ] && \
616 error="<p class="error">$(gettext "Bad login or pass")</p>"
617 header
618 html_header
619 user_box
620 login_page
621 html_footer ;;
623 *\ signup\ *)
624 # The login page
625 d="$(gettext "Sign Up")"
626 header
627 html_header
628 user_box
629 echo "<h2>$d</h2>"
630 if [ "$ONLINE_SIGNUP" == "yes" ]; then
631 signup_page
632 else
633 gettext "Online registration is disabled"
634 fi
635 html_footer ;;
637 *\ logout\ *)
638 # Set a Cookie in the past to logout.
639 expires="Expires=Wed, 01-Jan-1980 00:00:00 GMT"
640 if check_auth; then
641 rm -f "$sessions/$user"
642 header "Location: $script" "Set-Cookie: auth=none; $expires; HttpOnly"
643 fi ;;
645 *\ user\ *)
646 # User profile
647 d="$(GET user)"
648 last="$(cat $PEOPLE/"$(GET user)"/last)"
649 header
650 html_header
651 user_box
652 . $PEOPLE/"$(GET user)"/account.conf
653 cat << EOT
654 <h2>$(get_gravatar $MAIL) $NAME</h2>
656 <pre>
657 $(gettext "User name :") $USER
658 $(gettext "Last login :") $last
659 EOT
660 if check_auth && [ "$(GET user)" == "$user" ]; then
661 auth_people
662 else
663 # check_auth will set VARS to current logged user: re-source
664 . $PEOPLE/"$(GET user)"/account.conf
665 public_people
666 fi
667 html_footer ;;
669 *\ hg\ *)
670 d="Hg Log"
671 header
672 html_header
673 user_box
674 [ "$HG" != "yes" ] && gettext "Hg is disabled" && exit 0
675 [ ! -x /usr/bin/hg ] && gettext "Hg is not installed" && exit 0
676 echo "<h2>$d</h2>"
677 case " $(GET hg) " in
678 *\ init\ *)
679 if check_auth; then
680 [ -d "$content/.hg" ] && exit 0
681 echo '<pre>'
682 gettext "Executing: hg init"; echo
683 cd $content/ && hg init
684 echo '[hooks]' > .hg/hgrc
685 echo 'incoming = hg update' >> .hg/hgrc
686 gettext "Adding current content and committing"; echo
687 [ ! -f "$wiki/index.txt" ] && default_index
688 hg add && hg commit -u "$NAME <$MAIL>" \
689 -m "Initial commit with current content"
690 echo '</pre>' && cd ..
691 fi ;;
692 esac
693 hg_log
694 html_footer ;;
696 *)
697 # Display requested page
698 d="$(GET d)"
699 [ "$d" ] || d=$index
700 header
701 html_header
702 user_box
703 get_lang
705 # Generate a default index on first run
706 if [ ! -f "$wiki/$index.txt" ]; then
707 if ! default_index; then
708 echo "<pre class='error'>Directory : content/ is not writeable</pre>"
709 html_footer && exit 0
710 fi
711 fi
713 # Check cache dir
714 if [ ! -w "$cache" ]; then
715 echo "<pre class='error'>Directory : cache/ is not writeable"
716 echo "Command : install -m 0777 -d $tiny/cache</pre>"
717 html_footer && exit 0
718 fi
720 # Hg warning if enabled but not initiated
721 if [ "$HG" == "yes" ] && [ ! -d "$content/.hg" ]; then
722 echo '<p class="error box">'
723 gettext "Mercurial is enabled but no repository found"
724 echo ": <a href='$script?hg=init'>Hg init</a>"
725 echo '</p>'
726 fi
728 # Wiki tools
729 wiki_tools
731 # Wiki document
732 if [ ! -f "$wiki/$d.txt" ]; then
733 echo "<h2>$d</h2>"
734 gettext "The document does not exist. You can create it or read the"
735 echo " <a href='$script?d=en/help'>help</a>"
736 else
737 if fgrep -q [NOWIKI] $wiki/$d.txt; then
738 cat $wiki/$d.txt | sed '/\[NOWIKI\]/'d
739 else
740 cat $wiki/$d.txt | wiki_parser
741 fi
742 fi
743 html_footer ;;
744 esac
746 exit 0