tinycm view index.cgi @ rev 118

Tiny edit
author Paul Issott <paul@slitaz.org>
date Sun Mar 19 23:24:18 2017 +0000 (2017-03-19)
parents 348289f0b48d
children 05ccbdd25ab5
line source
1 #!/bin/sh
2 #
3 # TinyCM - Small, fast and elegant CGI/SHell Content Manager
4 #
5 # Copyright (C) 2012-2017 SliTaz GNU/Linux - BSD License
6 #
7 . /usr/lib/slitaz/httphelper.sh
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 add 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 grep -w -q "$user" ${ADMIN_USERS}
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 && chmod 0600 $AUTH_FILE
248 fi
249 echo "$user:$pass" >> $AUTH_FILE
250 mkdir -pm0700 $PEOPLE/${user}
251 cat > $PEOPLE/$user/account.conf << EOT
252 # User configuration
253 NAME="$name"
254 USER="$user"
255 MAIL="$mail"
256 EOT
257 chmod 0600 $PEOPLE/$user/account.conf
258 # First created user is admin
259 if [ $(ls ${PEOPLE} | wc -l) == "1" ]; then
260 echo "$user" > ${ADMIN_USERS}
261 fi
262 }
264 # The CM style parser. Just a title, simple text formatting and internal
265 # links, as well as images and use HTML for other stuff. Keep it fast!
266 # To make TinyCM as easy as possible we have a small HTML editor/helper
267 # written in Javascript
268 wiki_parser() {
269 doc="[0-9a-zA-Z\.\#/~\_%=\?\&,\+\:@;!\(\)\*\$'\-]*"
270 sed \
271 -e s"#====\([^']*\)====#<h2>\1</h2>#"g \
272 -e s"#===\([^']*\)===#<h3>\1</h3>#"g \
273 -e s"#==\([^']*\)==#<h4>\1</h4>#"g \
274 -e s"#\*\*\([^']*\)\*\*#<b>\1</b>#"g \
275 -e s"#''\([^']*\)''#<em>\1</em>#"g \
276 -e s"#__\([^']*\)__#<u>\1</u>#"g \
277 -e s"#\[\([^]]*\)|\($doc\)\]#<a href='$script?d=\2'>\1</a>#"g \
278 -e s"#\[\([^]]*\)!\($doc\)\]#<a href='\2'>\1</a>#"g \
279 -e s"#\[\(http://*[^]]*.png\)\]#<img src='\1' />#"g \
280 -e s"#\[\([^]]*.png\)\]#<img src='content/cloud/\1' />#"g \
281 -e s"#@\([^']*\)@#<a href='$script?user=\1'>\1</a>#"g
282 }
284 link_user() {
285 echo "<a href='$(basename $script)?user=$user'>$user</a>"
286 }
288 # Save a document. Do we need more than 1 backup and diff ?
289 save_document() {
290 mkdir -p $cache/$d $(dirname $wiki/$d)
291 # May be a new page.
292 if [ ! -f "$wiki/$d.txt" ]; then
293 new=0
294 touch $wiki/$d.txt
295 fi
296 cp $wiki/$d.txt $cache/$d/last.bak
297 sed "s/$(echo -en '\r') /\n/g" > $wiki/$d.txt << EOT
298 $(GET content)
299 EOT
300 diff $cache/$d/last.bak $wiki/$d.txt > $cache/$d/last.diff
301 # Log
302 if [ "$new" ]; then
303 echo "Page created by: $(link_user)" | log
304 echo "New document: <a href='$script?d=$d'>$d</a>" | log_activity
305 if [ "$HG" == "yes" ]; then
306 cd $content && hg -q add
307 hg commit -q -u "$NAME <$MAIL>" -m "Created new document: $d"
308 cd $tiny
309 fi
310 else
311 # Here we may clean log: cat && tail -n 40
312 echo "Page edited by: $(link_user)" | log
313 if [ "$HG" == "yes" ]; then
314 cd $content && hg commit -q -u "$NAME <$MAIL>" \
315 -m "Edited document: $d"
316 cd $tiny
317 fi
318 fi
319 }
321 # CM tools (edit, diff, etc) for auth users
322 wiki_tools() {
323 if check_auth; then
324 cat << EOT
325 <div id="tools">
326 <a href="$script?edit=$d">$(gettext "Edit document")</a>
327 <a href="$script?log=$d">$(gettext "File log")</a>
328 <a href="$script?diff=$d">$(gettext "Last diff")</a>
329 $PLUGINS_TOOLS
330 EOT
331 [ "$HG" == "yes" ] && echo "<a href='$script?hg'>Hg Log</a>"
332 echo "</div>"
333 fi
334 }
336 # Built-in tools such as log/ls and PLUGINS_TOOLS
337 tiny_tools() {
338 if check_auth; then
339 cat << EOT
340 <div id='tools'>
341 <a href='$script?log'>Activity log</a>
342 <a href='$script?ls'>Pages list</a>
343 $PLUGINS_TOOLS
344 </div>
345 EOT
346 fi
347 }
349 # Get and display Gravatar image: get_gravatar email size
350 # Link to profile: <a href="http://www.gravatar.com/$md5">...</a>
351 get_gravatar() {
352 email=$1
353 size=$2
354 [ "$size" ] || size=48
355 url="http://www.gravatar.com/avatar"
356 md5=$(md5crypt $email)
357 echo "<img src='$url/$md5?d=identicon&s=$size' alt='&lowast;' />"
358 }
360 # List hg logs
361 hg_log() {
362 cd $content
363 cat << EOT
364 <table>
365 <thead>
366 <td>$(gettext "User")</td>
367 <td>$(gettext "Description")</td>
368 <td>$(gettext "Revision")</td>
369 </thead>
370 EOT
371 hg log --template "<tr><td>{author}</td><td>{desc}</td><td>{rev}</td></tr>\n"
372 echo '</table>'
373 }
375 #
376 # POST actions
377 #
379 case " $(POST) " in
380 *\ auth\ *)
381 # Authenticate user. Create a session file in $sessions to be used
382 # by check_auth. We have the user login name and a peer session
383 # md5 string in the COOKIE.
384 user="$(POST auth)"
385 pass="$(md5crypt "$(POST pass)")"
386 valid=$(fgrep "${user}:" $AUTH_FILE | cut -d ":" -f 2)
387 if [ "$pass" == "$valid" ] && [ "$pass" != "" ]; then
388 md5session=$(echo -n "$$:$user:$pass:$$" | md5sum | awk '{print $1}')
389 [ -d $sessions ] || mkdir -p $sessions
390 date '+%Y-%m-%d' > ${PEOPLE}/${user}/last
391 echo "$md5session" > $sessions/$user
392 header "Location: $script" \
393 "Set-Cookie: auth=$user:$md5session; HttpOnly"
394 else
395 header "Location: $script?login&error"
396 fi ;;
397 *\ signup\ *)
398 # POST action for signup
399 name="$(POST name)"
400 user="$(POST user)"
401 mail="$(POST mail)"
402 pass="$(md5crypt "$(POST pass)")"
403 if ! grep "^${user}:" $AUTH_FILE; then
404 new_user_config
405 header "Location: $script?login"
406 else
407 header
408 html_header
409 user_box
410 echo "<h2>$(gettext 'User already exists:') $user</h2>"
411 html_footer
412 fi ;;
413 esac
415 #
416 # Plugins
417 #
418 for p in $(ls -1 $plugins)
419 do
420 [ -f "$plugins/$p/$p.conf" ] && . $plugins/$p/$p.conf
421 [ -x "$plugins/$p/$p.cgi" ] && . $plugins/$p/$p.cgi
422 done
424 #
425 # GET actions
426 #
428 case " $(GET) " in
429 *\ edit\ *)
430 d="$(GET edit)"
431 header
432 html_header
433 user_box
434 get_lang
435 wiki_tools
436 if check_auth; then
437 cat << EOT
438 <h2>$(gettext "Edit $doc [ $i18n ]")</h2>
440 <div id="edit">
442 <form method="get" action="$script" name="editor">
443 <input type="hidden" name="save" value="$d" />
444 <textarea name="content">$(cat "$wiki/$d.txt")</textarea>
445 <input type="submit" value="$(gettext "Save document")" />
446 $(gettext "Code Helper:")
447 $(cat lib/jseditor.html)
448 </form>
450 </div>
451 EOT
452 else
453 gettext "You must be logged in to edit pages"
454 fi
455 html_footer ;;
457 *\ save\ *)
458 d="$(GET save)"
459 if check_auth; then
460 save_document
461 fi
462 header "Location: $script?d=$d" ;;
464 *\ log\ *)
465 d="$(GET log)"
466 header
467 html_header
468 user_box
469 # Main activity
470 if [ "$d" == "log" ]; then
471 tiny_tools
472 echo "<h2>$(gettext "Activity log")</h2>"
473 echo '<pre>'
474 if [ -f "$cache/log/activity.log" ]; then
475 IFS="|"
476 tac $cache/log/activity.log | while read USER DATE LOG
477 do
478 . ${PEOPLE}/${USER}/account.conf
479 cat << EOT
480 <a href='$script?user=$USER'>$(get_gravatar $MAIL 24)</a>\
481 <span class='date'>$DATE -</span> $LOG
482 EOT
483 done
484 unset IFS
485 else
486 gettext "No activity log yet"; echo
487 fi
488 echo '</pre>'
489 html_footer && exit 0
490 fi
491 # Document activity
492 get_lang
493 wiki_tools
494 echo "<h2>$(gettext "Activity for:") <a href='$script?d=$d'>$d</a></h2>"
495 echo '<pre>'
496 if [ -f "$cache/$d/activity.log" ]; then
497 tac $cache/$d/activity.log
498 else
499 gettext "No log for: $d"; echo
500 fi
501 echo '</pre>'
502 html_footer ;;
504 *\ ls\ *)
505 d="Document list"
506 header
507 html_header
508 user_box
509 tiny_tools
510 [ ! check_auth ] && auth=0
511 echo "<h2>$(gettext "Pages list")</h2>"
512 echo '<pre>'
513 cd ${wiki}
514 for d in $(find . -type f | sed s'/.\///')
515 do
516 echo -n "<a href='$script?d=${d%.txt}'>${d%.txt}</a>"
517 if [ "$auth" ]; then
518 cat << EOT
519 : <a href="$script?edit=$d">$(gettext "Edit")</a> || \
520 <a href="$script?rm=$d">$(gettext "Remove")</a>
521 EOT
522 else
523 echo ""
524 fi
525 done && unset auth
526 echo '</pre>'
527 html_footer ;;
529 *\ rm\ *)
530 [ ! check_auth ] && exit 1
531 d="$(GET rm)"
532 rm ${wiki}/"${d}"
533 rm -rf ${cache}/"${d%.txt}"
534 header "Location: $script?ls" ;;
536 *\ diff\ *)
537 d="$(GET diff)"
538 date="last"
539 header
540 html_header
541 user_box
542 get_lang
543 wiki_tools
544 echo "<h2>$(gettext "Diff for:") <a href='$script?d=$d'>$d</a></h2>"
545 echo '<pre>'
546 if [ -f "$cache/$d/$date.diff" ]; then
547 cat $cache/$d/$date.diff | sed \
548 -e 's|&|\&amp;|g' -e 's|<|\&lt;|g' -e 's|>|\&gt;|g' \
549 -e s"#^-\([^']*\).#<span style='color: red;'>\0</span>#"g \
550 -e s"#^+\([^']*\).#<span style='color: green;'>\0</span>#"g \
551 -e s"#@@\([^']*\)@@#<span style='color: blue;'>@@\1@@</span>#"g
552 else
553 gettext "No diff for:"; echo " $d"
554 fi
555 echo '</pre>'
556 html_footer ;;
558 *\ login\ *)
559 # The login page
560 d="Login"
561 [ "$(GET error)" ] && \
562 error="<p class="error">$(gettext "Bad login or pass")</p>"
563 header
564 html_header
565 user_box
566 login_page
567 html_footer ;;
569 *\ signup\ *)
570 # The login page
571 d="$(gettext "Sign Up")"
572 header
573 html_header
574 user_box
575 echo "<h2>$d</h2>"
576 if [ "$ONLINE_SIGNUP" == "yes" ]; then
577 signup_page
578 else
579 gettext "Online registration is disabled"
580 fi
581 html_footer ;;
583 *\ logout\ *)
584 # Set a Cookie in the past to logout.
585 expires="Expires=Wed, 01-Jan-1980 00:00:00 GMT"
586 if check_auth; then
587 rm -f "$sessions/$user"
588 header "Location: $script" "Set-Cookie: auth=none; $expires; HttpOnly"
589 fi ;;
591 *\ user\ *)
592 # Basic user profile. Use the users plugin for more functions
593 d="$(GET user)"
594 last="$(cat $PEOPLE/"$(GET user)"/last)"
595 header
596 html_header
597 user_box
598 . $PEOPLE/"$(GET user)"/account.conf
599 cat << EOT
600 <h2>$(get_gravatar $MAIL) $NAME</h2>
602 <pre>
603 $(gettext "User name :") $USER
604 $(gettext "Last login :") $last
605 </pre>
606 EOT
607 html_footer ;;
609 *\ hg\ *)
610 d="Hg Log"
611 header
612 html_header
613 user_box
614 [ "$HG" != "yes" ] && gettext "Hg is disabled" && exit 0
615 [ ! -x /usr/bin/hg ] && gettext "Hg is not installed" && exit 0
616 echo "<h2>$d</h2>"
617 case " $(GET hg) " in
618 *\ init\ *)
619 if check_auth; then
620 [ -d "$content/.hg" ] && exit 0
621 echo '<pre>'
622 gettext "Executing: hg init"; echo
623 cd $content/ && hg init
624 echo '[hooks]' > .hg/hgrc
625 echo 'incoming = hg update' >> .hg/hgrc
626 gettext "Adding current content and committing"; echo
627 [ ! -f "$wiki/index.txt" ] && default_index
628 hg add && hg commit -u "$NAME <$MAIL>" \
629 -m "Initial commit with current content"
630 echo '</pre>' && cd ..
631 fi ;;
632 esac
633 hg_log
634 html_footer ;;
636 *)
637 # Display requested page
638 d="$(GET d)"
639 [ "$d" ] || d=$index
640 header
641 html_header
642 user_box
643 get_lang
645 # Generate a default index on first run
646 if [ ! -f "$wiki/$index.txt" ]; then
647 if ! default_index; then
648 echo "<pre class='error'>Directory : content/ is not writeable</pre>"
649 html_footer && exit 0
650 fi
651 fi
653 # Check cache dir
654 if [ ! -w "$cache" ]; then
655 echo "<pre class='error'>Directory : cache/ is not writeable"
656 echo "Command : install -m 0777 -d $tiny/cache</pre>"
657 html_footer && exit 0
658 fi
660 # Hg warning if enabled but not initiated
661 if [ "$HG" == "yes" ] && [ ! -d "$content/.hg" ]; then
662 echo '<p class="error box">'
663 gettext "Mercurial is enabled but no repository found"
664 echo ": <a href='$script?hg=init'>Hg init</a>"
665 echo '</p>'
666 fi
668 # Wiki tools
669 wiki_tools
671 # Wiki document
672 if [ ! -f "$wiki/$d.txt" ]; then
673 echo "<h2>$d</h2>"
674 gettext "The document does not exist. You can create it or read the"
675 echo " <a href='$script?d=en/help'>help</a>"
676 else
677 if fgrep -q [NOWIKI] $wiki/$d.txt; then
678 cat $wiki/$d.txt | sed '/\[NOWIKI\]/'d
679 else
680 cat $wiki/$d.txt | wiki_parser
681 fi
682 fi
683 html_footer ;;
684 esac
686 exit 0