Changes On Branch orefkovs-change
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch orefkovs-change Excluding Merge-Ins

This is equivalent to a diff from ab9b449190 to 102bfeae46

2013-02-19
05:15
merge with trunk Leaf check-in: 102bfeae46 user: orefkov tags: orefkovs-change
2013-02-18
22:35
Update version of OpenSSL that is referred to in the makefiles. Leaf check-in: ab9b449190 user: mistachkin tags: trunk
13:46
Fixed ticket [5df2715635b99bd46a] (check-in count mismatch). check-in: b27c0d6d3f user: stephan tags: trunk
2013-01-31
05:10
Очередное слияние с транком check-in: 738c8b3779 user: orefkov tags: orefkovs-change

Changes to Makefile.classic.

    34     34   #    for building intermediate code-generator tools.
    35     35   #
    36     36   #TCC = gcc -O6
    37     37   #TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
    38     38   TCC = gcc -g -Os -Wall
    39     39   
    40     40   # To add support for HTTPS
    41         -TCC += -DFOSSIL_ENABLE_SSL
           41  +#TCC += -DFOSSIL_ENABLE_SSL
    42     42   
    43     43   #### Extra arguments for linking the finished binary.  Fossil needs
    44     44   #    to link against the Z-Lib compression library.  There are no
    45     45   #    other dependencies.  We sometimes add the -static option here
    46     46   #    so that we can build a static executable that will run in a
    47     47   #    chroot jail.
    48     48   #
    49     49   LIB = -lz $(LDFLAGS)
    50     50   
    51     51   # If using HTTPS:
    52         -LIB += -lcrypto -lssl
           52  +#LIB += -lcrypto -lssl
    53     53   
    54     54   #### Tcl shell for use in running the fossil testsuite.  If you do not
    55     55   #    care about testing the end result, this can be blank.
    56     56   #
    57     57   TCLSH = tclsh
    58     58   
    59     59   # You should not need to change anything below this line

Changes to src/attach.c.

    47     47        "       comment, user,"
    48     48        "       (SELECT uuid FROM blob WHERE rid=attachid), attachid"
    49     49        "  FROM attachment",
    50     50        -1
    51     51     );
    52     52     if( zPage ){
    53     53       if( g.perm.RdWiki==0 ) login_needed();
    54         -    style_header("Attachments To %h", zPage);
           54  +    style_header(
           55  +#ifdef LANG_RU
           56  +      "Файлы к %h",
           57  +#elif LANG_EN
           58  +      "Attachments To %h",
           59  +#endif
           60  +      zPage);
    55     61       blob_appendf(&sql, " WHERE target=%Q", zPage);
    56     62     }else if( zTkt ){
    57     63       if( g.perm.RdTkt==0 ) login_needed();
    58         -    style_header("Attachments To Ticket %.10s", zTkt);
           64  +    style_header(
           65  +#ifdef LANG_RU
           66  +      "Файлы к задаче %.10s",
           67  +#elif LANG_EN
           68  +      "Attachments To Ticket %.10s",
           69  +#endif
           70  +      zTkt);
    59     71       blob_appendf(&sql, " WHERE target GLOB '%q*'", zTkt);
    60     72     }else{
    61     73       if( g.perm.RdTkt==0 && g.perm.RdWiki==0 ) login_needed();
    62         -    style_header("All Attachments");
           74  +    style_header(
           75  +#ifdef LANG_RU
           76  +      "Все файлы"
           77  +#elif LANG_EN
           78  +      "All Attachments"
           79  +#endif
           80  +      );
    63     81     }
    64     82     blob_appendf(&sql, " ORDER BY mtime DESC");
    65     83     db_prepare(&q, "%s", blob_str(&sql));
    66     84     @ <ol>
    67     85     while( db_step(&q)==SQLITE_ROW ){
    68     86       const char *zDate = db_column_text(&q, 0);
    69     87       const char *zSrc = db_column_text(&q, 1);
................................................................................
    89    107       }
    90    108       @ <li><p>
    91    109       @ Attachment %z(href("%R/ainfo/%s",zUuid))%S(zUuid)</a>
    92    110       if( moderation_pending(attachid) ){
    93    111         @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
    94    112       }
    95    113       @ <br><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
    96         -    @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
          114  +    @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">
          115  +#ifdef LANG_RU
          116  +    @ скачать
          117  +#elif LANG_EN
          118  +    @ download
          119  +#endif
          120  +    @ </a>]<br />
    97    121       if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
    98    122       if( zComment && zComment[0] ){
    99    123         @ %w(zComment)<br />
   100    124       }
   101    125       if( zPage==0 && zTkt==0 ){
   102    126         if( zSrc==0 || zSrc[0]==0 ){
          127  +#ifdef LANG_RU
          128  +        zSrc = "Удалён из";
          129  +#elif LANG_EN
   103    130           zSrc = "Deleted from";
          131  +#endif
   104    132         }else {
          133  +#ifdef LANG_RU
          134  +        zSrc = "Добавлен к";
          135  +#elif LANG_EN
   105    136           zSrc = "Added to";
          137  +#endif
   106    138         }
   107    139         if( strlen(zTarget)==UUID_SIZE && validate16(zTarget, UUID_SIZE) ){
   108         -        @ %s(zSrc) ticket <a href="%s(g.zTop)/tktview?name=%s(zTarget)">
          140  +#ifdef LANG_RU
          141  +          @ %s(zSrc) задач%s(zSrc==0 || zSrc[0]==0 ? "и" : "е")
          142  +#elif LANG_EN
          143  +        @ %s(zSrc) ticket
          144  +#endif
          145  +        @ <a href="%s(g.zTop)/tktview?name=%s(zTarget)">
   109    146           @ %S(zTarget)</a>
   110    147         }else{
   111         -        @ %s(zSrc) wiki page <a href="%s(g.zTop)/wiki?name=%t(zTarget)">
          148  +#ifdef LANG_RU
          149  +        @ %s(zSrc) страниц%s(zSrc==0 || zSrc[0]==0 ? "ы" : "е")
          150  +#elif LANG_EN
          151  +        @ %s(zSrc) wiki page
          152  +#endif
          153  +        @ <a href="%s(g.zTop)/wiki?name=%t(zTarget)">
   112    154           @ %h(zTarget)</a>
   113    155         }
   114    156       }else{
   115    157         if( zSrc==0 || zSrc[0]==0 ){
          158  +#ifdef LANG_RU
          159  +        @ Удалён
          160  +#elif LANG_EN
   116    161           @ Deleted
          162  +#endif
   117    163         }else {
          164  +#ifdef LANG_RU
          165  +        @ Добавлен
          166  +#elif LANG_EN
   118    167           @ Added
          168  +#endif
   119    169         }
   120    170       }
          171  +#ifdef LANG_RU
          172  +    @ пользователем %h(zDispUser)
          173  +#elif LANG_EN
   121    174       @ by %h(zDispUser) on
          175  +#endif
   122    176       hyperlink_to_date(zDate, ".");
   123    177       free(zUrlTail);
   124    178     }
   125    179     db_finalize(&q);
   126    180     @ </ol>
   127    181     style_footer();
   128    182     return;
................................................................................
   171    225          "SELECT coalesce(src,'x') FROM attachment"
   172    226          " WHERE target=%Q AND filename=%Q"
   173    227          " ORDER BY mtime DESC LIMIT 1",
   174    228          zTarget, zFile
   175    229       );
   176    230     }
   177    231     if( zUUID==0 || zUUID[0]==0 ){
          232  +#ifdef LANG_RU
          233  +    style_header("Нет файлов");
          234  +    @ Нет файлов....
          235  +#elif LANG_EN
   178    236       style_header("No Such Attachment");
   179    237       @ No such attachment....
          238  +#endif
   180    239       style_footer();
   181    240       return;
   182    241     }else if( zUUID[0]=='x' ){
          242  +#ifdef LANG_RU
          243  +    style_header("Отсутствует");
          244  +    @ Файл был удален
          245  +#elif LANG_EN
   183    246       style_header("Missing");
   184    247       @ Attachment has been deleted
          248  +#endif
   185    249       style_footer();
   186    250       return;
   187    251     }
   188    252     g.perm.Read = 1;
   189    253     cgi_replace_parameter("name",zUUID);
   190    254     if( fossil_strcmp(g.zPath,"attachview")==0 ){
   191    255       artifact_page();
................................................................................
   235    299     const char *zFrom = P("from");
   236    300     const char *aContent = P("f");
   237    301     const char *zName = PD("f:filename","unknown");
   238    302     const char *zTarget;
   239    303     const char *zTargetType;
   240    304     int szContent = atoi(PD("f:bytes","0"));
   241    305     int goodCaptcha = 1;
   242         -
          306  +  const char *zComment = PD("comment", "");
          307  + 
   243    308     if( P("cancel") ) cgi_redirect(zFrom);
   244    309     if( zPage && zTkt ) fossil_redirect_home();
   245    310     if( zPage==0 && zTkt==0 ) fossil_redirect_home();
   246    311     login_check_credentials();
   247    312     if( zPage ){
   248    313       if( g.perm.ApndWiki==0 || g.perm.Attach==0 ) login_needed();
   249    314       if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zPage) ){
   250    315         fossil_redirect_home();
   251    316       }
   252    317       zTarget = zPage;
   253         -    zTargetType = mprintf("Wiki Page <a href=\"%s/wiki?name=%h\">%h</a>",
   254         -                           g.zTop, zPage, zPage);
          318  +    zTargetType = mprintf(
          319  +#ifdef LANG_RU
          320  +      "странице"
          321  +#elif LANG_EN
          322  +      "Wiki Page"
          323  +#endif
          324  +      " <a href=\"%s/wiki?name=%h\">%h</a>",
          325  +        g.zTop, zPage, zPage);
   255    326     }else{
   256    327       if( g.perm.ApndTkt==0 || g.perm.Attach==0 ) login_needed();
   257    328       if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTkt) ){
   258    329         zTkt = db_text(0, "SELECT substr(tagname,5) FROM tag" 
   259    330                           " WHERE tagname GLOB 'tkt-%q*'", zTkt);
   260    331         if( zTkt==0 ) fossil_redirect_home();
   261    332       }
   262    333       zTarget = zTkt;
   263         -    zTargetType = mprintf("Ticket <a href=\"%s/tktview/%S\">%S</a>",
          334  +    zTargetType = mprintf(
          335  +#ifdef LANG_RU
          336  +      "задаче"
          337  +#elif LANG_EN
          338  +      "Ticket"
          339  +#endif
          340  +      " <a href=\"%s/tktview/%S\">%S</a>",
   264    341                             g.zTop, zTkt, zTkt);
   265    342     }
   266    343     if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
   267    344     if( P("cancel") ){
   268    345       cgi_redirect(zFrom);
   269    346     }
   270    347     if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){
   271    348       Blob content;
   272    349       Blob manifest;
   273    350       Blob cksum;
   274    351       char *zUUID;
   275         -    const char *zComment;
   276    352       char *zDate;
   277    353       int rid;
   278    354       int i, n;
   279    355       int addCompress = 0;
   280    356       Manifest *pManifest;
   281    357       int needModerator;
   282    358   
................................................................................
   298    374       for(i=n=0; zName[i]; i++){
   299    375         if( zName[i]=='/' || zName[i]=='\\' ) n = i;
   300    376       }
   301    377       zName += n;
   302    378       if( zName[0]==0 ) zName = "unknown";
   303    379       blob_appendf(&manifest, "A %F%s %F %s\n",
   304    380                    zName, addCompress ? ".gz" : "", zTarget, zUUID);
   305         -    zComment = PD("comment", "");
   306    381       while( fossil_isspace(zComment[0]) ) zComment++;
   307    382       n = strlen(zComment);
   308    383       while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; }
   309    384       if( n>0 ){
   310    385         blob_appendf(&manifest, "C %F\n", zComment);
   311    386       }
   312    387       zDate = date_in_standard_format("now");
................................................................................
   315    390       md5sum_blob(&manifest, &cksum);
   316    391       blob_appendf(&manifest, "Z %b\n", &cksum);
   317    392       attach_put(&manifest, rid, needModerator);
   318    393       assert( blob_is_reset(&manifest) );
   319    394       db_end_transaction(0);
   320    395       cgi_redirect(zFrom);
   321    396     }
          397  +#ifdef LANG_RU
          398  +  style_header("Добавить файл");
          399  +#elif LANG_EN
   322    400     style_header("Add Attachment");
          401  +#endif
   323    402     if( !goodCaptcha ){
          403  +#ifdef LANG_RU
          404  +    @ <p class="generalError">Ошибка: неверный код.</p>
          405  +#elif LANG_EN
   324    406       @ <p class="generalError">Error: Incorrect security code.</p>
          407  +#endif
   325    408     }
          409  +#ifdef LANG_RU
          410  +  @ <h2>Добавление файла к %s(zTargetType)</h2>
          411  +#elif LANG_EN
   326    412     @ <h2>Add Attachment To %s(zTargetType)</h2>
          413  +#endif
   327    414     form_begin("enctype='multipart/form-data'", "%R/attachadd");
   328    415     @ <div>
          416  +
          417  +#ifdef LANG_RU
          418  +  @ Файл для добавления:
          419  +#elif LANG_EN
   329    420     @ File to Attach:
   330         -  @ <input type="file" name="f" size="60" /><br />
          421  +#endif
          422  +  @ <input type="file" name="f" size="120" /><br />
          423  +#ifdef LANG_RU
          424  +  @ Описание:<br />
          425  +#elif LANG_EN
   331    426     @ Description:<br />
   332         -  @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
          427  +#endif
          428  +  @ <textarea name="comment" cols="80" rows="5" wrap="virtual">%h(zComment)</textarea><br />
   333    429     if( zTkt ){
   334    430       @ <input type="hidden" name="tkt" value="%h(zTkt)" />
   335    431     }else{
   336    432       @ <input type="hidden" name="page" value="%h(zPage)" />
   337    433     }
   338    434     @ <input type="hidden" name="from" value="%h(zFrom)" />
          435  +#ifdef LANG_RU
          436  +  @ <input type="submit" name="ok" value="Добавить файл" />
          437  +  @ <input type="submit" name="cancel" value="Отмена" />
          438  +#elif LANG_EN
   339    439     @ <input type="submit" name="ok" value="Add Attachment" />
   340    440     @ <input type="submit" name="cancel" value="Cancel" />
          441  +#endif
   341    442     @ </div>
   342    443     captcha_generate();
   343    444     @ </form>
   344    445     style_footer();
   345    446   }
   346    447   
   347    448   /*
................................................................................
   431    532       blob_appendf(&manifest, "D %s\n", zDate);
   432    533       blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody");
   433    534       md5sum_blob(&manifest, &cksum);
   434    535       blob_appendf(&manifest, "Z %b\n", &cksum);
   435    536       rid = content_put(&manifest);
   436    537       manifest_crosslink(rid, &manifest);
   437    538       db_end_transaction(0);
          539  +#ifdef LANG_RU
          540  +    @ <p>Указанный файл будет удален.</p>
          541  +#elif LANG_EN
   438    542       @ <p>The attachment below has been deleted.</p>
          543  +#endif
   439    544     }
   440    545   
   441    546     if( P("del")
   442    547      && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
   443    548     ){
   444    549       form_begin(0, "%R/ainfo/%s", zUuid);
          550  +#ifdef LANG_RU
          551  +    @ <p>Подтвердите удаление указанного файла.
          552  +    @ <input type="submit" name="confirm" value="Подтверждаю">
          553  +#elif LANG_EN
   445    554       @ <p>Confirm you want to delete the attachment shown below.
   446    555       @ <input type="submit" name="confirm" value="Confirm">
          556  +#endif
   447    557       @ </form>
   448    558     }
   449    559   
   450    560     isModerator = (zTktUuid && g.perm.ModTkt) || (zWikiName && g.perm.ModWiki);
   451    561     if( isModerator && (zModAction = P("modaction"))!=0 ){
   452    562       if( strcmp(zModAction,"delete")==0 ){
   453    563         moderation_disapprove(rid);
................................................................................
   458    568         }
   459    569         return;
   460    570       }
   461    571       if( strcmp(zModAction,"approve")==0 ){
   462    572         moderation_approve(rid);
   463    573       }
   464    574     }
          575  +#ifdef LANG_RU
          576  +  style_header("Информация о вложении");
          577  +  style_submenu_element("Как есть", "Как есть", "%R/artifact/%S", zUuid);
          578  +
          579  +  @ <div class="section">Обзор</div>
          580  +  @ <p><table class="label-value">
          581  +  @ <tr><th>ID&nbsp;артефакта:</th>
          582  +#elif LANG_EN
   465    583     style_header("Attachment Details");
   466    584     style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid);
   467    585   
   468    586     @ <div class="section">Overview</div>
   469    587     @ <p><table class="label-value">
   470    588     @ <tr><th>Artifact&nbsp;ID:</th>
          589  +#endif
   471    590     @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
   472    591     if( g.perm.Setup ){
   473    592       @ (%d(rid))
   474    593     }
   475    594     modPending = moderation_pending(rid);
   476    595     if( modPending ){
          596  +#ifdef LANG_RU
          597  +    @ <span class="modpending">*** Ожидает утверждения модератором ***</span>
          598  +#elif LANG_EN
   477    599       @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
          600  +#endif
   478    601     }
   479    602     if( zTktUuid ){
          603  +#ifdef LANG_RU
          604  +    @ <tr><th>Задача:</th>
          605  +#elif LANG_EN
   480    606       @ <tr><th>Ticket:</th>
          607  +#endif
   481    608       @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr>
   482    609     }
   483    610     if( zWikiName ){
          611  +#ifdef LANG_RU
          612  +    @ <tr><th>Вики-страница:</th>
          613  +#elif LANG_EN
   484    614       @ <tr><th>Wiki&nbsp;Page:</th>
          615  +#endif
   485    616       @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a></td></tr>
   486    617     }
          618  +#ifdef LANG_RU
          619  +  @ <tr><th>Дата:</th><td>
          620  +#elif LANG_EN
   487    621     @ <tr><th>Date:</th><td>
          622  +#endif
   488    623     hyperlink_to_date(zDate, "</td></tr>");
   489    624     free(zDate);
          625  +#ifdef LANG_RU
          626  +  @ <tr><th>Пользователь:</th><td>
          627  +#elif LANG_EN
   490    628     @ <tr><th>User:</th><td>
          629  +#endif
   491    630     hyperlink_to_user(pAttach->zUser, zDate, "</td></tr>");
          631  +#ifdef LANG_RU
          632  +  @ <tr><th>Артефакт&nbsp;:</th>
          633  +#elif LANG_EN
   492    634     @ <tr><th>Artifact&nbsp;Attached:</th>
          635  +#endif
   493    636     @ <td>%z(href("%R/artifact/%s",zSrc))%s(zSrc)</a>
   494    637     if( g.perm.Setup ){
   495    638       @ (%d(ridSrc))
   496    639     }
          640  +#ifdef LANG_RU
          641  +  @ <tr><th>Имя файла:</th><td>%h(zName)</td></tr>
          642  +#elif LANG_EN
   497    643     @ <tr><th>Filename:</th><td>%h(zName)</td></tr>
          644  +#endif
   498    645     zMime = mimetype_from_name(zName);
   499    646     if( g.perm.Setup ){
   500    647       @ <tr><th>MIME-Type:</th><td>%h(zMime)</td></tr>
   501    648     }
          649  +#ifdef LANG_RU
          650  +  @ <tr><th valign="top">Описание:</th><td valign="top">%h(zDesc)</td></tr>
          651  +#elif LANG_EN
   502    652     @ <tr><th valign="top">Description:</th><td valign="top">%h(zDesc)</td></tr>
          653  +#endif
   503    654     @ </table>
   504    655     
   505    656     if( isModerator && modPending ){
          657  +#ifdef LANG_RU
          658  +    @ <div class="section">Модерация</div>
          659  +#elif LANG_EN
   506    660       @ <div class="section">Moderation</div>
          661  +#endif
   507    662       @ <blockquote>
   508    663       form_begin(0, "%R/ainfo/%s", zUuid);
   509    664       @ <label><input type="radio" name="modaction" value="delete">
          665  +#ifdef LANG_RU
          666  +    @ Удалить это изменение</label><br />
          667  +#elif LANG_EN
   510    668       @ Delete this change</label><br />
          669  +#endif
   511    670       @ <label><input type="radio" name="modaction" value="approve">
          671  +#ifdef LANG_RU
          672  +    @ Утвердить это изменение</label><br />
          673  +#elif LANG_EN
   512    674       @ Approve this change</label><br />
          675  +#endif
   513    676       @ <input type="submit" value="Submit">
   514    677       @ </form>
   515    678       @ </blockquote>
   516    679     }
   517    680   
          681  +#ifdef LANG_RU
          682  +  @ <div class="section">Содержимое</div>
          683  +#elif LANG_EN
   518    684     @ <div class="section">Content Appended</div>
          685  +#endif
   519    686     @ <blockquote>
   520    687     blob_zero(&attach);
   521    688     if( zMime==0 || strncmp(zMime,"text/", 5)==0 ){
   522    689       const char *z;
   523    690       const char *zLn = P("ln");
   524    691       content_get(ridSrc, &attach);
   525    692       blob_to_utf8_no_bom(&attach, 0);
................................................................................
   529    696       }else{
   530    697         @ <pre>
   531    698         @ %h(z)
   532    699         @ </pre>
   533    700       }
   534    701     }else if( strncmp(zMime, "image/", 6)==0 ){
   535    702       @ <img src="%R/raw/%S(zSrc)?m=%s(zMime)"></img>
   536         -    style_submenu_element("Image", "Image", "%R/raw/%S?m=%s", zSrc, zMime);
          703  +    style_submenu_element("Изображение", "Изображение", "%R/raw/%S?m=%s", zSrc, zMime);
   537    704     }else{
   538    705       int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
   539    706       @ <i>(file is %d(sz) bytes of binary data)</i>
   540    707     }
   541    708     @ </blockquote>
   542    709     manifest_destroy(pAttach);
   543    710     blob_reset(&attach);
................................................................................
   570    737       const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
   571    738       if( cnt==0 ){
   572    739         @ %s(zHeader)
   573    740       }
   574    741       cnt++;
   575    742       @ <li>
   576    743       @ %z(href("%R/artifact/%s",zSrc))%h(zFile)</a>
          744  +#ifdef LANG_RU
          745  +    @ добавлено %h(zDispUser) 
          746  +#elif LANG_EN
   577    747       @ added by %h(zDispUser) on
          748  +#endif
   578    749       hyperlink_to_date(zDate, ".");
          750  +#ifdef LANG_RU
          751  +    @ [%z(href("%R/ainfo/%s",zUuid))детали</a>]
          752  +#elif LANG_EN
   579    753       @ [%z(href("%R/ainfo/%s",zUuid))details</a>]
          754  +#endif
   580    755       @ </li>
   581    756     }
   582    757     if( cnt ){
   583    758       @ </ul>
   584    759     }
   585    760     db_finalize(&q);
   586    761     
   587    762   }

Changes to src/cgi.c.

  1240   1240         cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal);
  1241   1241   #if 0
  1242   1242       }else if( fossil_strcmp(zFieldName,"referer:")==0 ){
  1243   1243         cgi_setenv("HTTP_REFERER", zVal);
  1244   1244   #endif
  1245   1245       }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
  1246   1246         cgi_setenv("HTTP_USER_AGENT", zVal);
         1247  +    }else if( fossil_strcmp(zFieldName,"x-real-ip:")==0 ){
         1248  +      cgi_replace_parameter("REMOTE_ADDR", mprintf("%s",zVal));
  1247   1249       }
  1248   1250     }
  1249   1251     cgi_init();
  1250   1252     cgi_trace(0);
  1251   1253   }
  1252   1254   
  1253   1255   #if INTERFACE

Changes to src/checkin.c.

   650    650     const char *zUuid,    /* The artifact ID of the ancestor */
   651    651     const char *zDate     /* Date & time of the current check-in */
   652    652   ){
   653    653   #ifndef FOSSIL_ALLOW_OUT_OF_ORDER_DATES
   654    654     int b;
   655    655     b = db_exists(
   656    656       "SELECT 1 FROM event"
   657         -    " WHERE datetime(mtime)>=%Q"
          657  +    " WHERE strftime('%%Y-%%m-%%dT%%H:%%M:%%f', mtime)>=strftime('%%Y-%%m-%%dT%%H:%%M:%%f', %Q)"
   658    658       "   AND type='ci' AND objid=%d",
   659    659       zDate, rid
   660    660     );
   661    661     if( b ){
   662    662       fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)"
   663    663                    " Use --allow-older to override.", zUuid, zDate);
   664    664     }

Changes to src/diff.c.

    43     43   #define DIFF_CONTEXT_EX   (((u64)0x04)<<32) /* Use context even if zero */
    44     44   #define DIFF_NOTTOOBIG    (((u64)0x08)<<32) /* Only display if not too big */
    45     45   
    46     46   /*
    47     47   ** These error messages are shared in multiple locations.  They are defined
    48     48   ** here for consistency.
    49     49   */
           50  +
    50     51   #define DIFF_CANNOT_COMPUTE_BINARY \
    51     52       "cannot compute difference between binary files\n"
    52     53   
    53     54   #define DIFF_CANNOT_COMPUTE_SYMLINK \
    54     55       "cannot compute difference between symlink and regular file\n"
    55     56   
    56     57   #define DIFF_TOO_MANY_CHANGES_TXT \
................................................................................
   707    708             p->iEnd = p->iEnd2;
   708    709             p->iEnd2 = 0;
   709    710           }
   710    711         }
   711    712       }
   712    713       if( c=='\t' ){
   713    714         z[j++] = ' ';
   714         -      while( (k&7)!=7 && k<w ){ z[j++] = ' '; k++; }
          715  +      while( (k&3)!=3 && k<w ){ z[j++] = ' '; k++; }
   715    716       }else if( c=='\r' || c=='\f' ){
   716    717         z[j++] = ' ';
   717    718       }else if( c=='<' && p->escHtml ){
   718    719         memcpy(&z[j], "&lt;", 4);
   719    720         j += 4;
   720    721       }else if( c=='&' && p->escHtml ){
   721    722         memcpy(&z[j], "&amp;", 5);

Changes to src/info.c.

    83     83     }
    84     84     if( showFamily ){
    85     85       db_prepare(&q, "SELECT uuid, pid, isprim FROM plink JOIN blob ON pid=rid "
    86     86                      " WHERE cid=%d"
    87     87                      " ORDER BY isprim DESC, mtime DESC /*sort*/", rid);
    88     88       while( db_step(&q)==SQLITE_ROW ){
    89     89         const char *zUuid = db_column_text(&q, 0);
    90         -      const char *zType = db_column_int(&q, 2) ? "parent:" : "merged-from:";
           90  +      const char *zType = db_column_int(&q, 2) ?
           91  +#ifdef LANG_RU
           92  +        "родитель:"
           93  +#elif LANG_EN
           94  +        "parent:"
           95  +#endif
           96  +        :
           97  +#ifdef LANG_RU
           98  +      "слито с:"
           99  +#elif LANG_EN
          100  +        "merged-from:"
          101  +#endif
          102  +          ;
    91    103         zDate = db_text("",
    92    104           "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
    93    105           db_column_int(&q, 1)
    94    106         );
    95    107         fossil_print("%-13s %s %s\n", zType, zUuid, zDate);
    96    108         free(zDate);
    97    109       }
    98    110       db_finalize(&q);
    99    111       db_prepare(&q, "SELECT uuid, cid, isprim FROM plink JOIN blob ON cid=rid "
   100    112                      " WHERE pid=%d"
   101    113                      " ORDER BY isprim DESC, mtime DESC /*sort*/", rid);
   102    114       while( db_step(&q)==SQLITE_ROW ){
   103    115         const char *zUuid = db_column_text(&q, 0);
   104         -      const char *zType = db_column_int(&q, 2) ? "child:" : "merged-into:";
          116  +      const char *zType = db_column_int(&q, 2) ?
          117  +#ifdef LANG_RU
          118  +        "потомок:"
          119  +#elif LANG_EN
          120  +        "child:"
          121  +#endif
          122  +        :
          123  +#ifdef LANG_RU
          124  +      "слито в:"
          125  +#elif LANG_EN
          126  +        "merged-into:"
          127  +#endif
          128  +        ;
   105    129         zDate = db_text("",
   106    130           "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
   107    131           db_column_int(&q, 1)
   108    132         );
   109    133         fossil_print("%-13s %s %s\n", zType, zUuid, zDate);
   110    134         free(zDate);
   111    135       }
   112    136       db_finalize(&q);
   113    137     }
   114    138     zTags = info_tags_of_checkin(rid, 0);
   115    139     if( zTags && zTags[0] ){
   116         -    fossil_print("tags:         %s\n", zTags);
          140  +    fossil_print(
          141  +#ifdef LANG_RU
          142  +      "теги:         %s\n",
          143  +#elif LANG_EN
          144  +      "tags:         %s\n",
          145  +#endif
          146  +      zTags);
   117    147     }
   118    148     free(zTags);
   119    149     if( zComment ){
   120         -    fossil_print("comment:      ");
          150  +    fossil_print(
          151  +#ifdef LANG_RU
          152  +      "комментарий:      "
          153  +#elif LANG_EN
          154  +      "comment:      "
          155  +#endif
          156  +      );
   121    157       comment_print(zComment, 14, 79);
   122    158       free(zComment);
   123    159     }
   124    160   }
   125    161   
   126    162   /*
   127    163   ** Print information about the URLs used to access a repository and
................................................................................
   459    495     ReCompiled *pRe = 0; /* regex */
   460    496   
   461    497     login_check_credentials();
   462    498     if( !g.perm.Read ){ login_needed(); return; }
   463    499     zName = P("name");
   464    500     rid = name_to_rid_www("name");
   465    501     if( rid==0 ){
   466         -    style_header("Check-in Information Error");
          502  +    style_header(
          503  +#ifdef LANG_RU
          504  +      "Ошибка информации о фиксации"
          505  +#elif LANG_EN
          506  +      "Check-in Information Error"
          507  +#endif
          508  +      );
          509  +#ifdef LANG_RU
          510  +    @ Объект не найден: %h(g.argv[2])
          511  +#elif LANG_EN
   467    512       @ No such object: %h(g.argv[2])
          513  +#endif
   468    514       style_footer();
   469    515       return;
   470    516     }
   471    517     zRe = P("regex");
   472    518     if( zRe ) re_compile(&pRe, zRe, 0);
   473    519     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   474    520     zParent = db_text(0,
................................................................................
   484    530        " WHERE blob.rid=%d"
   485    531        "   AND event.objid=%d",
   486    532        rid, rid
   487    533     );
   488    534     sideBySide = atoi(PD("sbs","1"));
   489    535     if( db_step(&q)==SQLITE_ROW ){
   490    536       const char *zUuid = db_column_text(&q, 0);
   491         -    char *zTitle = mprintf("Check-in [%.10s]", zUuid);
          537  +    char *zTitle = mprintf(
          538  +#ifdef LANG_RU
          539  +      "Фиксация [%.10s]",
          540  +#elif LANG_EN
          541  +      "Check-in [%.10s]",
          542  +#endif
          543  +      zUuid);
   492    544       char *zEUser, *zEComment;
   493    545       const char *zUser;
   494    546       const char *zComment;
   495    547       const char *zDate;
   496    548       const char *zOrigDate;
   497    549   
   498    550       style_header(zTitle);
................................................................................
   504    556       zEComment = db_text(0,
   505    557                      "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
   506    558                      TAG_COMMENT, rid);
   507    559       zUser = db_column_text(&q, 2);
   508    560       zComment = db_column_text(&q, 3);
   509    561       zDate = db_column_text(&q,1);
   510    562       zOrigDate = db_column_text(&q, 4);
          563  +#ifdef LANG_RU
          564  +    @ <div class="section">Обзор</div>
          565  +#elif LANG_EN
   511    566       @ <div class="section">Overview</div>
          567  +#endif
   512    568       @ <table class="label-value">
   513         -    @ <tr><th>SHA1&nbsp;Hash:</th><td>%s(zUuid)
          569  +    @ <tr><th>
          570  +#ifdef LANG_RU
          571  +    @ SHA1&nbsp;хэш:
          572  +#elif LANG_EN
          573  +    @ SHA1&nbsp;Hash:
          574  +#endif
          575  +    @ </th><td>%s(zUuid)
   514    576       if( g.perm.Setup ){
          577  +#ifdef LANG_RU
          578  +      @ (ID записи: %d(rid))
          579  +#elif LANG_EN
   515    580         @ (Record ID: %d(rid))
          581  +#endif
   516    582       }
   517    583       @ </td></tr>
   518         -    @ <tr><th>Date:</th><td>
          584  +#ifdef LANG_RU
          585  +      @ <tr><th>Дата:</th><td>
          586  +#elif LANG_EN
          587  +      @ <tr><th>Date:</th><td>
          588  +#endif
   519    589       hyperlink_to_date(zDate, "</td></tr>");
   520    590       if( zOrigDate && fossil_strcmp(zDate, zOrigDate)!=0 ){
          591  +#ifdef LANG_RU
          592  +      @ <tr><th>Оригинальная&nbsp;дата:</th><td>
          593  +#elif LANG_EN
   521    594         @ <tr><th>Original&nbsp;Date:</th><td>
          595  +#endif
   522    596         hyperlink_to_date(zOrigDate, "</td></tr>");
   523    597       }
   524    598       if( zEUser ){
          599  +#ifdef LANG_RU
          600  +      @ <tr><th>Исправлявший&nbsp;пользователь:</th><td>
          601  +#elif LANG_EN
   525    602         @ <tr><th>Edited&nbsp;User:</th><td>
          603  +#endif
   526    604         hyperlink_to_user(zEUser,zDate,"</td></tr>");
          605  +#ifdef LANG_RU
          606  +      @ <tr><th>Первый&nbsp;пользователь:</th><td>
          607  +#elif LANG_EN
   527    608         @ <tr><th>Original&nbsp;User:</th><td>
          609  +#endif
   528    610         hyperlink_to_user(zUser,zDate,"</td></tr>");
   529    611       }else{
          612  +#ifdef LANG_RU
          613  +      @ <tr><th>Пользователь:</th><td>
          614  +#elif LANG_EN
   530    615         @ <tr><th>User:</th><td>
          616  +#endif
   531    617         hyperlink_to_user(zUser,zDate,"</td></tr>");
   532    618       }
   533    619       if( zEComment ){
          620  +#ifdef LANG_RU
          621  +      @ <tr><th>Исправленный&nbsp;комментарий:</th><td>%w(zEComment)</td></tr>
          622  +      @ <tr><th>Первоначальный&nbsp;комментарий:</th><td>%w(zComment)</td></tr>
          623  +#elif LANG_EN
   534    624         @ <tr><th>Edited&nbsp;Comment:</th><td>%w(zEComment)</td></tr>
   535    625         @ <tr><th>Original&nbsp;Comment:</th><td>%w(zComment)</td></tr>
          626  +#endif
   536    627       }else{
          628  +#ifdef LANG_RU
          629  +      @ <tr><th>Comment:</th><td>%w(zComment)</td></tr>
          630  +#elif LANG_EN
   537    631         @ <tr><th>Comment:</th><td>%w(zComment)</td></tr>
          632  +#endif
   538    633       }
   539    634       if( g.perm.Admin ){
   540    635         db_prepare(&q,
   541    636            "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)"
   542    637            "  FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)"
   543    638            " WHERE blob.rid=%d",
   544    639            rid
   545    640         );
   546    641         if( db_step(&q)==SQLITE_ROW ){
   547    642           const char *zIpAddr = db_column_text(&q, 0);
   548    643           const char *zUser = db_column_text(&q, 1);
   549    644           const char *zDate = db_column_text(&q, 2);
   550    645           if( zUser==0 || zUser[0]==0 ) zUser = "unknown";
          646  +#ifdef LANG_RU
          647  +        @ <tr><th>Получено&nbsp;от:</th>
          648  +        @ <td>%h(zUser) @ %h(zIpAddr) %s(zDate)</td></tr>
          649  +#elif LANG_EN
   551    650           @ <tr><th>Received&nbsp;From:</th>
   552    651           @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
          652  +#endif
   553    653         }
   554    654         db_finalize(&q);
   555    655       }
   556    656       if( g.perm.Hyperlink ){
   557    657         const char *zProjName = db_get("project-name", "unnamed");
          658  +#ifdef LANG_RU
          659  +      @ <tr><th>События:</th><td>
          660  +      @   %z(href("%R/timeline?f=%S",zUuid))ближние</a>
          661  +      if( zParent ){
          662  +        @ | %z(href("%R/timeline?p=%S",zUuid))родители</a>
          663  +      }
          664  +      if( !isLeaf ){
          665  +        @ | %z(href("%R/timeline?d=%S",zUuid))потомки</a>
          666  +      }
          667  +      if( zParent && !isLeaf ){
          668  +        @ | %z(href("%R/timeline?dp=%S",zUuid))обои</a>
          669  +      }
          670  +#elif LANG_EN
   558    671         @ <tr><th>Timelines:</th><td>
   559    672         @   %z(href("%R/timeline?f=%S",zUuid))family</a>
   560    673         if( zParent ){
   561    674           @ | %z(href("%R/timeline?p=%S",zUuid))ancestors</a>
   562    675         }
   563    676         if( !isLeaf ){
   564    677           @ | %z(href("%R/timeline?d=%S",zUuid))descendants</a>
   565    678         }
   566    679         if( zParent && !isLeaf ){
   567    680           @ | %z(href("%R/timeline?dp=%S",zUuid))both</a>
   568    681         }
          682  +#endif
   569    683         db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
   570    684                        " WHERE rid=%d AND tagtype>0 "
   571    685                        "   AND tag.tagid=tagxref.tagid "
   572    686                        "   AND +tag.tagname GLOB 'sym-*'", rid);
   573    687         while( db_step(&q)==SQLITE_ROW ){
   574    688           const char *zTagName = db_column_text(&q, 0);
   575    689           @  | %z(href("%R/timeline?r=%T",zTagName))%h(zTagName)</a>
................................................................................
   576    690         }
   577    691         db_finalize(&q);
   578    692   
   579    693   
   580    694         /* The Download: line */
   581    695         if( g.perm.Zip ){
   582    696           char *zUrl = mprintf("%R/tarball/%t-%S.tar.gz?uuid=%s",
   583         -                             zProjName, zUuid, zUuid);
          697  +          zProjName, zUuid, zUuid);
   584    698           @ </td></tr>
   585    699           @ <tr><th>Downloads:</th><td>
   586    700           @ %z(href("%s",zUrl))Tarball</a>
   587    701           @ | %z(href("%R/zip/%t-%S.zip?uuid=%s",zProjName,zUuid,zUuid))
          702  +#ifdef LANG_RU
          703  +        @         ZIP архив</a>
          704  +#elif LANG_EN
   588    705           @         ZIP archive</a>
          706  +#endif
   589    707           fossil_free(zUrl);
   590    708         }
   591         -      @ </td></tr>
          709  +	  @ </td></tr>
          710  +#ifdef LANG_RU
          711  +		  @ <tr><th>Другие&nbsp;ссылки:</th>
          712  +		  @   <td>
          713  +		  @     %z(href("%R/dir?ci=%S",zUuid))файлы</a>
          714  +		  @   | %z(href("%R/artifact/%S",zUuid))манифест</a>
          715  +#elif LANG_EN
   592    716         @ <tr><th>Other&nbsp;Links:</th>
   593    717         @   <td>
   594    718         @     %z(href("%R/dir?ci=%S",zUuid))files</a>
   595    719         @   | %z(href("%R/fileage?name=%S",zUuid))file ages</a>
   596    720         @   | %z(href("%R/artifact/%S",zUuid))manifest</a>
          721  +#endif
   597    722         if( g.perm.Write ){
          723  +#ifdef LANG_RU
          724  +        @   | %z(href("%R/ci_edit?r=%S",zUuid))изменить</a>
          725  +#elif LANG_EN
   598    726           @   | %z(href("%R/ci_edit?r=%S",zUuid))edit</a>
          727  +#endif
   599    728         }
   600    729         @   </td>
   601    730         @ </tr>
   602    731       }
   603    732       @ </table>
   604    733     }else{
   605         -    style_header("Check-in Information");
          734  +    style_header(
          735  +#ifdef LANG_RU
          736  +      "Информация о фиксации"
          737  +#elif LANG_EN
          738  +      "Check-in Information"
          739  +#endif
          740  +      );
   606    741       login_anonymous_available();
   607    742     }
   608    743     db_finalize(&q);
   609    744     showTags(rid, "");
   610    745     if( zParent ){
          746  +#ifdef LANG_RU
          747  +    @ <div class="section">Изменения</div>
          748  +#elif LANG_EN
   611    749       @ <div class="section">Changes</div>
          750  +#endif
   612    751       @ <div class="sectionmenu">
   613    752       showDiff = g.zPath[0]!='c';
   614    753       if( db_get_boolean("show-version-diffs", 0)==0 ){
   615    754         showDiff = !showDiff;
   616    755         if( showDiff ){
   617    756           @ %z(xhref("class='button'","%R/vinfo/%T",zName))
          757  +#ifdef LANG_RU
          758  +        @ спрятать&nbsp;различия</a>
          759  +#elif LANG_EN
   618    760           @ hide&nbsp;diffs</a>
          761  +#endif
   619    762           if( sideBySide ){
   620    763             @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName))
          764  +#ifdef LANG_RU
          765  +          @ различия&nbsp;разом</a>
          766  +#elif LANG_EN
   621    767             @ unified&nbsp;diffs</a>
          768  +#endif
   622    769           }else{
   623    770             @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName))
          771  +#ifdef LANG_RU
          772  +          @ различия&nbsp;бок-о-бок</a>
          773  +#elif LANG_EN
   624    774             @ side-by-side&nbsp;diffs</a>
          775  +#endif
   625    776           }
   626    777         }else{
   627    778           @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName))
          779  +#ifdef LANG_RU
          780  +        @ показать&nbsp;различия&nbsp;разом</a>
          781  +#elif LANG_EN
   628    782           @ show&nbsp;unified&nbsp;diffs</a>
          783  +#endif
   629    784           @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName))
          785  +#ifdef LANG_RU
          786  +        @ показать&nbsp;различия&nbsp;бок-о-бок</a>
          787  +#elif LANG_EN
   630    788           @ show&nbsp;side-by-side&nbsp;diffs</a>
          789  +#endif
   631    790         }
   632    791       }else{
   633    792         if( showDiff ){
   634         -        @ %z(xhref("class='button'","%R/ci/%T",zName))hide&nbsp;diffs</a>
          793  +        @ %z(xhref("class='button'","%R/ci/%T",zName))
          794  +#ifdef LANG_RU
          795  +        @ спрятать&nbsp;различия</a>
          796  +#elif LANG_EN
          797  +        @ hide&nbsp;diffs</a>
          798  +#endif
   635    799           if( sideBySide ){
   636    800             @ %z(xhref("class='button'","%R/info/%T?sbs=0",zName))
          801  +#ifdef LANG_RU
          802  +          @ различия&nbsp;разом</a>
          803  +#elif LANG_EN
   637    804             @ unified&nbsp;diffs</a>
          805  +#endif
   638    806           }else{
   639    807             @ %z(xhref("class='button'","%R/info/%T?sbs=1",zName))
          808  +#ifdef LANG_RU
          809  +          @ различия&nbsp;бок-о-бок</a>
          810  +#elif LANG_EN
   640    811             @ side-by-side&nbsp;diffs</a>
          812  +#endif
   641    813           }
   642    814         }else{
   643    815           @ %z(xhref("class='button'","%R/vinfo/%T?sbs=0",zName))
          816  +#ifdef LANG_RU
          817  +          @ показать&nbsp;различия&nbsp;разом</a>
          818  +#elif LANG_EN
   644    819           @ show&nbsp;unified&nbsp;diffs</a>
          820  +#endif
   645    821           @ %z(xhref("class='button'","%R/vinfo/%T?sbs=1",zName))
          822  +#ifdef LANG_RU
          823  +        @ показать&nbsp;различия&nbsp;бок-о-бок</a>
          824  +#elif LANG_EN
   646    825           @ show&nbsp;side-by-side&nbsp;diffs</a>
          826  +#endif
   647    827         }
   648    828       }
          829  +    @ &nbsp;&nbsp;
   649    830       @ %z(xhref("class='button'","%R/vpatch?from=%S&to=%S",zParent,zUuid))
   650         -    @ patch</a></div>
          831  +#ifdef LANG_RU
          832  +    @ патч</a>
          833  +#elif LANG_EN
          834  +    @ patch</a>
          835  +#endif
          836  +    @</div>
          837  +    
   651    838       if( pRe ){
   652    839         @ <p><b>Only differences that match regular expression "%h(zRe)"
   653    840         @ are shown.</b></p>
   654    841       }
   655    842       db_prepare(&q,
   656    843          "SELECT name,"
   657    844          "       mperm,"
................................................................................
   694    881     int modPending;
   695    882     const char *zModAction;
   696    883   
   697    884     login_check_credentials();
   698    885     if( !g.perm.RdWiki ){ login_needed(); return; }
   699    886     rid = name_to_rid_www("name");
   700    887     if( rid==0 || (pWiki = manifest_get(rid, CFTYPE_WIKI))==0 ){
   701         -    style_header("Wiki Page Information Error");
          888  +    style_header(
          889  +#ifdef LANG_RU
          890  +      "Ошибка информации о странице"
          891  +#elif LANG_EN
          892  +      "Wiki Page Information Error"
          893  +#endif
          894  +      );
          895  +#ifdef LANG_RU
          896  +    @ Объект не найден: %h(g.argv[2])
          897  +#elif LANG_EN
   702    898       @ No such object: %h(P("name"))
          899  +#endif
   703    900       style_footer();
   704    901       return;
   705    902     }
   706    903     if( g.perm.ModWiki && (zModAction = P("modaction"))!=0 ){
   707    904       if( strcmp(zModAction,"delete")==0 ){
   708    905         moderation_disapprove(rid);
   709    906         cgi_redirectf("%R/wiki?name=%T", pWiki->zWikiTitle);
   710    907         /*NOTREACHED*/
   711    908       }
   712    909       if( strcmp(zModAction,"approve")==0 ){
   713    910         moderation_approve(rid);
   714    911       }
   715    912     }
          913  +#ifdef LANG_RU
          914  +  style_header("Правка \"%h\"", pWiki->zWikiTitle);
          915  +#elif LANG_EN
   716    916     style_header("Update of \"%h\"", pWiki->zWikiTitle);
          917  +#endif
   717    918     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   718    919     zDate = db_text(0, "SELECT datetime(%.17g)", pWiki->rDate);
          920  +#ifdef LANG_RU
          921  +  style_submenu_element("Как есть", "Как есть", "artifact/%S", zUuid);
          922  +  style_submenu_element("История", "История", "whistory?name=%t",
          923  +                        pWiki->zWikiTitle);
          924  +  style_submenu_element("Страница", "Страница", "wiki?name=%t",
          925  +                        pWiki->zWikiTitle);
          926  +#elif LANG_EN
   719    927     style_submenu_element("Raw", "Raw", "artifact/%S", zUuid);
   720    928     style_submenu_element("History", "History", "whistory?name=%t",
   721    929                           pWiki->zWikiTitle);
   722    930     style_submenu_element("Page", "Page", "wiki?name=%t",
   723    931                           pWiki->zWikiTitle);
          932  +#endif
   724    933     login_anonymous_available();
          934  +#ifdef LANG_RU
          935  +  @ <div class="section">Обзор</div>
          936  +#elif LANG_EN
   725    937     @ <div class="section">Overview</div>
          938  +#endif
   726    939     @ <p><table class="label-value">
          940  +#ifdef LANG_RU
          941  +  @ <tr><th>ID&nbsp;артефакта:</th>
          942  +#elif LANG_EN
   727    943     @ <tr><th>Artifact&nbsp;ID:</th>
          944  +#endif
   728    945     @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
   729    946     if( g.perm.Setup ){
   730    947       @ (%d(rid))
   731    948     }
   732    949     modPending = moderation_pending(rid);
   733    950     if( modPending ){
          951  +#ifdef LANG_RU
          952  +    @ <span class="modpending">*** Ожидает утверждения модератором ***</span>
          953  +#elif LANG_EN
   734    954       @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
          955  +#endif
   735    956     }
   736    957     @ </td></tr>
          958  +#ifdef LANG_RU
          959  +  @ <tr><th>Имя&nbsp;страницы:</th><td>%h(pWiki->zWikiTitle)</td></tr>
          960  +  @ <tr><th>Дата:</th><td>
          961  +  hyperlink_to_date(zDate, "</td></tr>");
          962  +  @ <tr><th>Создавший&nbsp;пользователь:</th><td>
          963  +#elif LANG_EN
   737    964     @ <tr><th>Page&nbsp;Name:</th><td>%h(pWiki->zWikiTitle)</td></tr>
   738    965     @ <tr><th>Date:</th><td>
   739    966     hyperlink_to_date(zDate, "</td></tr>");
   740    967     @ <tr><th>Original&nbsp;User:</th><td>
          968  +#endif
   741    969     hyperlink_to_user(pWiki->zUser, zDate, "</td></tr>");
   742    970     if( pWiki->nParent>0 ){
   743    971       int i;
          972  +#ifdef LANG_RU
          973  +    @ <tr><th>Родител%s(pWiki->nParent==1?"ь":"и"):</th><td>
          974  +#elif LANG_EN
   744    975       @ <tr><th>Parent%s(pWiki->nParent==1?"":"s"):</th><td>
          976  +#endif
   745    977       for(i=0; i<pWiki->nParent; i++){
   746    978         char *zParent = pWiki->azParent[i];
   747    979         @ %z(href("info/%S",zParent))%s(zParent)</a>
   748    980       }
   749    981       @ </td></tr>
   750    982     }
   751    983     @ </table>
   752    984   
   753    985     if( g.perm.ModWiki && modPending ){
          986  +#ifdef LANG_RU
          987  +    @ <div class="section">Модерация</div>
          988  +#elif LANG_EN
   754    989       @ <div class="section">Moderation</div>
          990  +#endif
   755    991       @ <blockquote>
   756    992       @ <form method="POST" action="%R/winfo/%s(zUuid)">
   757    993       @ <label><input type="radio" name="modaction" value="delete">
          994  +#ifdef LANG_RU
          995  +    @ Удалить это изменение</label><br />
          996  +#elif LANG_EN
   758    997       @ Delete this change</label><br />
          998  +#endif
   759    999       @ <label><input type="radio" name="modaction" value="approve">
         1000  +#ifdef LANG_RU
         1001  +    @ Утвердить это изменение</label><br />
         1002  +#elif LANG_EN
   760   1003       @ Approve this change</label><br />
         1004  +#endif
         1005  +#ifdef LANG_RU
         1006  +    @ <input type="submit" value="Ок">
         1007  +#elif LANG_EN
   761   1008       @ <input type="submit" value="Submit">
         1009  +#endif
   762   1010       @ </form>
   763   1011       @ </blockquote>
   764   1012     }
   765   1013   
   766   1014   
         1015  +#ifdef LANG_RU
         1016  +  @ <div class="section">Содержимое</div>
         1017  +#elif LANG_EN
   767   1018     @ <div class="section">Content</div>
         1019  +#endif
   768   1020     blob_init(&wiki, pWiki->zWiki, -1);
   769   1021     wiki_convert(&wiki, 0, 0);
   770   1022     blob_reset(&wiki);
   771   1023     manifest_destroy(pWiki);
   772   1024     style_footer();
   773   1025   }
   774   1026   
................................................................................
   777   1029   */
   778   1030   void webpage_error(const char *zFormat, ...){
   779   1031     va_list ap;
   780   1032     const char *z;
   781   1033     va_start(ap, zFormat);
   782   1034     z = vmprintf(zFormat, ap);
   783   1035     va_end(ap);
         1036  +#ifdef LANG_RU
         1037  +  style_header("Ошибка URL");
         1038  +  @ <h1>Ошибка</h1>
         1039  +#elif LANG_EN
   784   1040     style_header("URL Error");
   785   1041     @ <h1>Error</h1>
         1042  +#endif
   786   1043     @ <p>%h(z)</p>
   787   1044     style_footer();
   788   1045   }
   789   1046   
   790   1047   /*
   791   1048   ** Find an checkin based on query parameter zParam and parse its
   792   1049   ** manifest.  Return the number of errors.
   793   1050   */
   794   1051   static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){
   795   1052     int rid;
   796   1053   
   797   1054     *pRid = rid = name_to_rid_www(zParam);
   798   1055     if( rid==0 ){
   799         -    const char *z = P(zParam);
   800         -    if( z==0 || z[0]==0 ){
   801         -      webpage_error("Missing \"%s\" query parameter.", zParam);
   802         -    }else{
   803         -      webpage_error("No such artifact: \"%s\"", z);
   804         -    }
   805         -    return 0;
         1056  +	  const char *z = P(zParam);
         1057  +	  if( z==0 || z[0]==0 ){
         1058  +		  webpage_error(
         1059  +#ifdef LANG_RU
         1060  +			  "Отсутствует параметр \"%s\" в запросе."
         1061  +#elif LANG_EN
         1062  +			  "Missing \"%s\" query parameter."
         1063  +#endif
         1064  +			  , zParam);
         1065  +	  }else{
         1066  +		  webpage_error(
         1067  +#ifdef LANG_RU
         1068  +			  "Артефакт не найден: \"%s\""
         1069  +#elif LANG_EN
         1070  +			  "No such artifact: \"%s\""
         1071  +#endif
         1072  +			  , z);
         1073  +	  }
         1074  +      return 0;
   806   1075     }
   807   1076     if( !is_a_version(rid) ){
   808         -    webpage_error("Artifact %s is not a checkin.", P(zParam));
         1077  +    webpage_error(
         1078  +#ifdef LANG_RU
         1079  +      "Артефакт не Фиксация.",
         1080  +#elif LANG_EN
         1081  +      "Artifact %s is not a checkin.",
         1082  +#endif
         1083  +      P(zParam));
   809   1084       return 0;
   810   1085     }
   811   1086     return manifest_get(rid, CFTYPE_MANIFEST);
   812   1087   }
   813   1088   
   814   1089   /*
   815   1090   ** Output a description of a check-in
................................................................................
   838   1113         wikiFlags |= WIKI_NOBLOCK;
   839   1114       }
   840   1115       hyperlink_to_uuid(zUuid);
   841   1116       blob_zero(&comment);
   842   1117       db_column_blob(&q, 2, &comment);
   843   1118       wiki_convert(&comment, 0, wikiFlags);
   844   1119       blob_reset(&comment);
         1120  +#ifdef LANG_RU
         1121  +    @ (пользователь:
         1122  +#elif LANG_EN
   845   1123       @ (user:
         1124  +#endif
   846   1125       hyperlink_to_user(zUser,zDate,",");
   847   1126       if( zTagList && zTagList[0] && g.perm.Hyperlink ){
   848   1127         int i;
   849   1128         const char *z = zTagList;
   850   1129         Blob links;
   851   1130         blob_zero(&links);
   852   1131         while( z && z[0] ){
................................................................................
   854   1133           blob_appendf(&links,
   855   1134                 "%z%#h</a>%.2s",
   856   1135                 href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i]
   857   1136           );
   858   1137           if( z[i]==0 ) break;
   859   1138           z += i+2;
   860   1139         }
         1140  +#ifdef LANG_RU
         1141  +      @ метка: %s(blob_str(&links)),
         1142  +#elif LANG_EN
   861   1143         @ tags: %s(blob_str(&links)),
         1144  +#endif
   862   1145         blob_reset(&links);
   863   1146       }else{
   864         -      @ tags: %h(zTagList),
         1147  +#ifdef LANG_RU
         1148  +      @ метка: %h(zTagList),
         1149  +#elif LANG_EN
         1150  +      @ метка: %h(zTagList),
         1151  +#endif
   865   1152       }
         1153  +#ifdef LANG_RU
         1154  +    @ дата:
         1155  +#elif LANG_EN
   866   1156       @ date:
         1157  +#endif
   867   1158       hyperlink_to_date(zDate, ")");
   868   1159     }
   869   1160     db_finalize(&q);
   870   1161   }
   871   1162   
   872   1163   
   873   1164   /*
................................................................................
   915   1206     if( pFrom==0 ) return;
   916   1207     sideBySide = atoi(PD("sbs","1"));
   917   1208     showDetail = atoi(PD("detail","0"));
   918   1209     if( !showDetail && sideBySide ) showDetail = 1;
   919   1210     zFrom = P("from");
   920   1211     zTo = P("to");
   921   1212     if( !sideBySide ){
   922         -    style_submenu_element("Side-by-side Diff", "sbsdiff",
         1213  +    style_submenu_element(
         1214  +#ifdef LANG_RU
         1215  +      "Различия бок-о-бок", "sbsdiff",
         1216  +#elif LANG_EN
         1217  +      "Side-by-side Diff", "sbsdiff",
         1218  +#endif
   923   1219                             "%R/vdiff?from=%T&to=%T&detail=%d&sbs=1",
   924   1220                             zFrom, zTo, showDetail);
   925   1221     }else{
   926         -    style_submenu_element("Unified Diff", "udiff",
         1222  +    style_submenu_element(
         1223  +#ifdef LANG_RU
         1224  +      "Различия разом", "udiff",
         1225  +#elif LANG_EN
         1226  +      "Unified Diff", "udiff",
         1227  +#endif
   927   1228                             "%R/vdiff?from=%T&to=%T&detail=%d&sbs=0",
   928   1229                             zFrom, zTo, showDetail);
   929   1230     }
   930   1231     style_submenu_element("Invert", "invert",
   931   1232                           "%R/vdiff?from=%T&to=%T&detail=%d&sbs=%d",
   932   1233                           zTo, zFrom, showDetail, sideBySide);
         1234  +#ifdef LANG_RU
         1235  +  style_header("Различия в фиксации");
         1236  +  @ <h2>Различия между:</h2><blockquote>
         1237  +  checkin_description(ridFrom);
         1238  +#elif LANG_EN
   933   1239     style_header("Check-in Differences");
   934   1240     @ <h2>Difference From:</h2><blockquote>
   935   1241     checkin_description(ridFrom);
   936         -  @ </blockquote><h2>To:</h2><blockquote>
         1242  +#endif
         1243  +  @ </blockquote><h2>
         1244  +#ifdef LANG_RU
         1245  +    @ и:
         1246  +#elif LANG_EN
         1247  +    @ To:
         1248  +#endif
         1249  +  @ </h2><blockquote>
   937   1250     checkin_description(ridTo);
   938   1251     @ </blockquote>
   939   1252     if( pRe ){
   940   1253       @ <p><b>Only differences that match regular expression "%h(zRe)"
   941   1254       @ are shown.</b></p>
   942   1255     }
   943   1256     @<hr /><p>
................................................................................
  1271   1584     if( zRe ) re_compile(&pRe, zRe, 0);
  1272   1585     content_get(v1, &c1);
  1273   1586     content_get(v2, &c2);
  1274   1587     text_diff(&c1, &c2, pOut, pRe, diffFlags);
  1275   1588     blob_reset(&c1);
  1276   1589     blob_reset(&c2);
  1277   1590     if( !isPatch ){
         1591  +#ifdef LANG_RU
         1592  +    style_header("Различия");
         1593  +#elif LANG_EN
  1278   1594       style_header("Diff");
  1279         -    style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
         1595  +#endif
         1596  +    style_submenu_element(
         1597  +#ifdef LANG_RU
         1598  +      "Патч", "Патч",
         1599  +#elif LANG_EN
         1600  +      "Patch", "Patch",
         1601  +#endif
         1602  +      "%s/fdiff?v1=%T&v2=%T&patch",
  1280   1603                             g.zTop, P("v1"), P("v2"));
  1281   1604       if( !sideBySide ){
  1282         -      style_submenu_element("Side-by-side Diff", "sbsdiff",
         1605  +      style_submenu_element(
         1606  +#ifdef LANG_RU
         1607  +        "Различия бок-о-бок", "sbsdiff",
         1608  +#elif LANG_EN
         1609  +        "Side-by-side Diff", "sbsdiff",
         1610  +#endif
  1283   1611                               "%s/fdiff?v1=%T&v2=%T&sbs=1",
  1284   1612                               g.zTop, P("v1"), P("v2"));
  1285   1613       }else{
  1286         -      style_submenu_element("Unified Diff", "udiff",
         1614  +      style_submenu_element(
         1615  +#ifdef LANG_RU
         1616  +        "Различия разом", "udiff",
         1617  +#elif LANG_EN
         1618  +        "Unified Diff", "udiff",
         1619  +#endif
  1287   1620                               "%s/fdiff?v1=%T&v2=%T&sbs=0",
  1288   1621                               g.zTop, P("v1"), P("v2"));
  1289   1622       }
  1290   1623   
  1291   1624       if( P("smhdr")!=0 ){
         1625  +#ifdef LANG_RU
         1626  +      @ <h2>Различия между артефактом
         1627  +#elif LANG_EN
  1292   1628         @ <h2>Differences From Artifact
  1293         -      @ %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a> To
         1629  +#endif
         1630  +      @ %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a>
         1631  +#ifdef LANG_RU
         1632  +      @ и
         1633  +#elif LANG_EN
         1634  +      @ To
         1635  +#endif
  1294   1636         @ %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>.</h2>
  1295   1637       }else{
  1296         -      @ <h2>Differences From
  1297         -      @ Artifact %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a>:</h2>
         1638  +#ifdef LANG_RU
         1639  +      @ <h2>Различия между артефактом
         1640  +#elif LANG_EN
         1641  +      @ <h2>Differences From Artifact
         1642  +#endif
         1643  +      @ %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a>:</h2>
  1298   1644         object_description(v1, 0, 0);
  1299         -      @ <h2>To Artifact %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>:</h2>
         1645  +      @ <h2>
         1646  +#ifdef LANG_RU
         1647  +        @ и артефактом
         1648  +#elif LANG_EN
         1649  +        @ To Artifact
         1650  +#endif
         1651  +      @ %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>:</h2>
  1300   1652         object_description(v2, 0, 0);
  1301   1653       }
  1302   1654       if( pRe ){
  1303   1655         @ <b>Only differences that match regular expression "%h(zRe)"
  1304   1656         @ are shown.</b>
  1305   1657       }
  1306   1658       @ <hr />
................................................................................
  1414   1766     rid = name_to_rid_www("name");
  1415   1767     login_check_credentials();
  1416   1768     if( !g.perm.Read ){ login_needed(); return; }
  1417   1769     if( rid==0 ) fossil_redirect_home();
  1418   1770     if( g.perm.Admin ){
  1419   1771       const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
  1420   1772       if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
  1421         -      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
         1773  +      style_submenu_element(
         1774  +#ifdef LANG_RU
         1775  +        "Восстановить","Восстановить",
         1776  +#elif LANG_EN
         1777  +        "Unshun","Unshun",
         1778  +#endif
         1779  +        "%s/shun?uuid=%s&sub=1",
  1422   1780               g.zTop, zUuid);
  1423   1781       }else{
  1424         -      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
         1782  +      style_submenu_element(
         1783  +#ifdef LANG_RU
         1784  +        "Стереть","Стереть",
         1785  +#elif LANG_EN
         1786  +        "Shun","Shun",
         1787  +#endif
         1788  +        "%s/shun?shun=%s#addshun",
  1425   1789               g.zTop, zUuid);
  1426   1790       }
  1427   1791     }
  1428         -  style_header("Hex Artifact Content");
         1792  +  style_header(
         1793  +#ifdef LANG_RU
         1794  +    "Содержимое бинарного артефакта"
         1795  +#elif LANG_EN
         1796  +    "Hex Artifact Content"
         1797  +#endif
         1798  +    );
  1429   1799     zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
  1430   1800     if( g.perm.Setup ){
         1801  +#ifdef LANG_RU
         1802  +    @ <h2>Артефакт %s(zUuid) (%d(rid)):</h2>
         1803  +  }else{
         1804  +    @ <h2>Artifact %s(zUuid):</h2>
         1805  +#elif LANG_EN
  1431   1806       @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
  1432   1807     }else{
  1433   1808       @ <h2>Artifact %s(zUuid):</h2>
         1809  +#endif
  1434   1810     }
  1435   1811     blob_zero(&downloadName);
  1436   1812     object_description(rid, 0, &downloadName);
         1813  +#ifdef LANG_RU
         1814  +  style_submenu_element("Скачать", "Скачать",
         1815  +#elif LANG_EN
  1437   1816     style_submenu_element("Download", "Download",
         1817  +#endif
  1438   1818           "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
  1439   1819     @ <hr />
  1440   1820     content_get(rid, &content);
  1441   1821     @ <blockquote><pre>
  1442   1822     hexdump(&content);
  1443   1823     @ </pre></blockquote>
  1444   1824     style_footer();
................................................................................
  1571   1951         style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
  1572   1952               g.zTop, zUuid);
  1573   1953       }else{
  1574   1954         style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
  1575   1955               g.zTop, zUuid);
  1576   1956       }
  1577   1957     }
         1958  +#ifdef LANG_RU
         1959  +  style_header("Содержимое артефакта");
         1960  +#elif LANG_EN
  1578   1961     style_header("Artifact Content");
         1962  +#endif
  1579   1963     zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
  1580   1964     if( g.perm.Setup ){
         1965  +#ifdef LANG_RU
         1966  +    @ <h2>Артефакт %s(zUuid) (%d(rid)):</h2>
         1967  +  }else{
         1968  +    @ <h2>Артефакт %s(zUuid):</h2>
         1969  +#elif LANG_EN
  1581   1970       @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
  1582   1971     }else{
  1583   1972       @ <h2>Artifact %s(zUuid):</h2>
         1973  +#endif
  1584   1974     }
         1975  +  @ <blockquote><p>
  1585   1976     blob_zero(&downloadName);
  1586   1977     objType = object_description(rid, 0, &downloadName);
         1978  +#ifdef LANG_RU
         1979  +  style_submenu_element("Скачать", "Скачать",
         1980  +#elif LANG_EN
  1587   1981     style_submenu_element("Download", "Download",
         1982  +#endif
  1588   1983             "%R/raw/%T?name=%s", blob_str(&downloadName), zUuid);
  1589   1984     if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
         1985  +#ifdef LANG_RU
         1986  +    style_submenu_element("Используется в фиксациях", "Используется в фиксациях",
         1987  +#elif LANG_EN
  1590   1988       style_submenu_element("Checkins Using", "Checkins Using",
         1989  +#endif
  1591   1990             "%R/timeline?uf=%s&n=200",zUuid);
  1592   1991     }
  1593   1992     asText = P("txt")!=0;
  1594   1993     zMime = mimetype_from_name(blob_str(&downloadName));
  1595   1994     if( zMime ){
  1596   1995       if( fossil_strcmp(zMime, "text/html")==0 ){
  1597   1996         if( asText ){
  1598   1997           style_submenu_element("Html", "Html",
  1599   1998                                 "%s/artifact/%s", g.zTop, zUuid);
  1600   1999         }else{
  1601   2000           renderAsHtml = 1;
  1602         -        style_submenu_element("Text", "Text",
         2001  +        style_submenu_element(
         2002  +#ifdef LANG_RU
         2003  +          "Текст", "Показать как текст",
         2004  +#elif LANG_EN
         2005  +          "Text", "Text",
         2006  +#endif
  1603   2007                                 "%s/artifact/%s?txt=1", g.zTop, zUuid);
  1604   2008         }
  1605   2009       }else if( fossil_strcmp(zMime, "application/x-fossil-wiki")==0 ){
  1606   2010         if( asText ){
  1607         -        style_submenu_element("Wiki", "Wiki",
         2011  +        style_submenu_element(
         2012  +#ifdef LANG_RU
         2013  +          "Вики", "Показать как вики",
         2014  +#elif LANG_EN
         2015  +          "Wiki", "Wiki",
         2016  +#endif
  1608   2017                                 "%s/artifact/%s", g.zTop, zUuid);
  1609   2018         }else{
  1610   2019           renderAsWiki = 1;
  1611         -        style_submenu_element("Text", "Text",
         2020  +        style_submenu_element(
         2021  +#ifdef LANG_RU
         2022  +          "Текст", "Показать как текст",
         2023  +#elif LANG_EN
         2024  +          "Text", "Text",
         2025  +#endif
  1612   2026                                 "%s/artifact/%s?txt=1", g.zTop, zUuid);
  1613   2027         }
  1614   2028       }
  1615   2029     }
  1616   2030     if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
  1617   2031       style_submenu_element("Parsed", "Parsed", "%R/info/%s", zUuid);
  1618   2032     }
................................................................................
  1642   2056           @ </pre>
  1643   2057         }
  1644   2058       }else if( strncmp(zMime, "image/", 6)==0 ){
  1645   2059         @ <img src="%R/raw/%S(zUuid)?m=%s(zMime)" />
  1646   2060         style_submenu_element("Image", "Image",
  1647   2061                               "%R/raw/%S?m=%s", zUuid, zMime);
  1648   2062       }else{
         2063  +#ifdef LANG_RU
         2064  +      @ <i>(бинарный файл, байт: %d(blob_size(&content)))</i>
         2065  +#elif LANG_EN
  1649   2066         @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i>
         2067  +#endif
  1650   2068       }
  1651   2069       @ </blockquote>
  1652   2070     }
  1653   2071     style_footer();
  1654   2072   }
  1655   2073   
  1656   2074   /*

Changes to src/login.c.

   491    491       zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
   492    492       if( db_int(1, "SELECT 0 FROM user"
   493    493                     " WHERE uid=%d"
   494    494                     " AND (constant_time_cmp(pw,%Q)=0"
   495    495                     "      OR constant_time_cmp(pw,%Q)=0)", 
   496    496                     g.userUid, zSha1Pw, zPasswd) ){
   497    497         sleep(1);
          498  +#ifdef LANG_RU
          499  +      zErrMsg = 
          500  +      @ <p><span class="loginError">
          501  +      @ Вы ввели неверный старый пароль.
          502  +      @ Ваш пароль не изменен.
          503  +      @ </span></p>
          504  +#elif LANG_EN
   498    505         zErrMsg = 
   499         -         @ <p><span class="loginError">
   500         -         @ You entered an incorrect old password while attempting to change
   501         -         @ your password.  Your password is unchanged.
   502         -         @ </span></p>
          506  +      @ <p><span class="loginError">
          507  +      @ You entered an incorrect old password while attempting to change
          508  +      @ your password.  Your password is unchanged.
          509  +      @ </span></p>
          510  +#endif
   503    511         ;
   504    512       }else if( fossil_strcmp(zNew1,zNew2)!=0 ){
          513  +#ifdef LANG_RU
          514  +      zErrMsg = 
          515  +       @ <p><span class="loginError">
          516  +       @ Два введенных новых пароля не совпадают.
          517  +       @ Ваш пароль не изменен.
          518  +       @ </span></p>
          519  +#elif LANG_EN
   505    520         zErrMsg = 
   506         -         @ <p><span class="loginError">
   507         -         @ The two copies of your new passwords do not match.
   508         -         @ Your password is unchanged.
   509         -         @ </span></p>
          521  +       @ <p><span class="loginError">
          522  +       @ The two copies of your new passwords do not match.
          523  +       @ Your password is unchanged.
          524  +       @ </span></p>
          525  +#endif
   510    526         ;
   511    527       }else{
   512    528         char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0);
   513    529         char *zChngPw;
   514    530         char *zErr;
   515    531         db_multi_exec(
   516    532            "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
................................................................................
   541    557     }
   542    558     if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
   543    559       /* Attempting to log in as a user other than anonymous.
   544    560       */
   545    561       uid = login_search_uid(zUsername, zPasswd);
   546    562       if( uid<=0 ){
   547    563         sleep(1);
          564  +#ifdef LANG_RU
   548    565         zErrMsg = 
   549    566            @ <p><span class="loginError">
          567  +         @ Вы ввели неверный пароль или имя несущестующего пользователя.
          568  +         @ </span></p>
          569  +#elif LANG_EN
          570  +      zErrMsg = 
          571  +        @ <p><span class="loginError">
   550    572            @ You entered an unknown user or an incorrect password.
   551    573            @ </span></p>
          574  +#endif
   552    575         ;
   553    576         record_login_attempt(zUsername, zIpAddr, 0);
   554    577       }else{
   555    578         /* Non-anonymous login is successful.  Set a cookie of the form:
   556    579         **
   557    580         **    HASH/PROJECT/LOGIN
   558    581         **
................................................................................
   559    582         ** where HASH is a random hex number, PROJECT is either project
   560    583         ** code prefix, and LOGIN is the user name.
   561    584         */
   562    585         login_set_user_cookie(zUsername, uid, NULL);
   563    586         redirect_to_g();
   564    587       }
   565    588     }
   566         -  style_header("Login/Logout");
          589  +  style_header(
          590  +#ifdef LANG_RU
          591  +    "Вход/Выход"
          592  +#elif LANG_EN
          593  +    "Login/Logout"
          594  +#endif
          595  +    );
   567    596     @ %s(zErrMsg)
   568    597     if( zGoto && P("anon")==0 ){
   569    598       @ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p>
   570    599     }
   571    600     form_begin(0, "%R/login");
   572    601     if( zGoto ){
   573    602       @ <input type="hidden" name="g" value="%h(zGoto)" />
   574    603     }
   575    604     @ <table class="login_out">
   576    605     @ <tr>
   577         -  @   <td class="login_out_label">User ID:</td>
          606  +  @   <td class="login_out_label">
          607  +#ifdef LANG_RU
          608  +  @ Пользователь:
          609  +#elif LANG_EN
          610  +  @ User ID:
          611  +#endif
          612  +  @ </td>
   578    613     if( anonFlag ){
   579    614       @ <td><input type="text" id="u" name="u" value="anonymous" size="30" /></td>
   580    615     }else{
   581    616       @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
   582    617     }
   583    618     @ </tr>
   584    619     @ <tr>
   585         -  @  <td class="login_out_label">Password:</td>
          620  +  @  <td class="login_out_label">
          621  +#ifdef LANG_RU
          622  +  @ Пароль:
          623  +#elif LANG_EN
          624  +  @ Password:
          625  +#endif
          626  +  @ </td>
   586    627     @   <td><input type="password" id="p" name="p" value="" size="30" /></td>
   587    628     @ </tr>
   588    629     if( g.zLogin==0 ){
   589    630       zAnonPw = db_text(0, "SELECT pw FROM user"
   590    631                            " WHERE login='anonymous'"
   591    632                            "   AND cap!=''");
   592    633     }
   593    634     @ <tr>
   594    635     @   <td></td>
          636  +#ifdef LANG_RU
          637  +  @   <td><input type="submit" name="in" value="Вход"
          638  +#elif LANG_EN
   595    639     @   <td><input type="submit" name="in" value="Login"
          640  +#endif
   596    641     @        onClick="chngAction(this.form)" /></td>
   597    642     @ </tr>
   598    643     @ </table>
   599    644     @ <script type="text/JavaScript">
   600    645     @   gebi('u').focus()
   601    646     @   function chngAction(form){
   602    647     if( g.sslNotAvailable==0
................................................................................
   606    651        char *zSSL = mprintf("https:%s", &g.zBaseURL[5]);
   607    652        @  if( form.u.value!="anonymous" ){
   608    653        @     form.action = "%h(zSSL)/login";
   609    654        @  }
   610    655     }
   611    656     @ }
   612    657     @ </script>
   613         -  if( g.zLogin==0 ){
   614         -    @ <p>Enter
   615         -  }else{
   616         -    @ <p>You are currently logged in as <b>%h(g.zLogin)</b></p>
   617         -    @ <p>To change your login to a different user, enter
   618         -  }
   619         -  @ your user-id and password at the left and press the
   620         -  @ "Login" button.  Your user name will be stored in a browser cookie.
   621         -  @ You must configure your web browser to accept cookies in order for
   622         -  @ the login to take.</p>
          658  +#ifdef LANG_RU
          659  +    if( g.zLogin==0 ){
          660  +      @ <p>Укажите
          661  +    }else{
          662  +      @ <p>Вы вошли как <b>%h(g.zLogin)</b></p>
          663  +      @ <p>Чтобы войти под другим именем, укажите
          664  +    }
          665  +    @ имя пользователя и пароль, и нажмите кнопку "Вход".
          666  +    @ Введенное имя будет сохранено в куках браузера.
          667  +    @ Вы должны разрешить браузеру сохранять куки для возможности входа
          668  +    @ на сайт.</p>
          669  +#elif LANG_EN
          670  +    if( g.zLogin==0 ){
          671  +      @ <p>Enter
          672  +    }else{
          673  +      @ <p>You are currently logged in as <b>%h(g.zLogin)</b></p>
          674  +      @ <p>To change your login to a different user, enter
          675  +    }
          676  +    @ your user-id and password at the left and press the
          677  +    @ "Login" button.  Your user name will be stored in a browser cookie.
          678  +    @ You must configure your web browser to accept cookies in order for
          679  +    @ the login to take.</p>
          680  +#endif
          681  +  
   623    682     if( db_get_boolean("self-register", 0) ){
          683  +#ifdef LANG_RU
          684  +    @ <p>Если у вас нет аккаунта, вы можете
          685  +    @ <a href="%s(g.zTop)/register?g=%T(P("G"))">зарегистрироваться</a>.
          686  +#elif LANG_EN
   624    687       @ <p>If you do not have an account, you can 
   625    688       @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>.
          689  +#endif
   626    690     }
          691  +  
   627    692     if( zAnonPw ){
   628    693       unsigned int uSeed = captcha_seed();
   629    694       char const *zDecoded = captcha_decode(uSeed);
   630    695       int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
   631    696       char *zCaptcha = captcha_render(zDecoded);
   632    697   
   633    698       @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
          699  +#ifdef LANG_RU
          700  +    @ Гости могут указать <b>anonymous</b> как имя пользователя
          701  +    @ и ввести 8-символьный пароль, указанный ниже:</p>
          702  +#elif LANG_EN
   634    703       @ Visitors may enter <b>anonymous</b> as the user-ID with
   635    704       @ the 8-character hexadecimal password shown below:</p>
          705  +#endif
   636    706       @ <div class="captcha"><table class="captcha"><tr><td><pre>
   637    707       @ %h(zCaptcha)
   638    708       @ </pre></td></tr></table>
   639    709       if( bAutoCaptcha ) {
   640         -        @ <input type="button" value="Fill out captcha"
          710  +#ifdef LANG_RU
          711  +      @ <input type="button" value="Заполнить капчу"
          712  +#elif LANG_EN
          713  +      @ <input type="button" value="Fill out captcha"
          714  +#endif
   641    715           @  onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" />
   642    716       }
   643    717       @ </div>
   644    718       free(zCaptcha);
   645    719     }
   646    720     if( g.zLogin ){
   647    721       @ <hr />
          722  +#ifdef LANG_RU
          723  +    @ <p>Для выхода
          724  +    @ нажмите эту кнопку:<br />
          725  +    @ <input type="submit" name="out" value="Выход" /></p>
          726  +#elif LANG_EN
   648    727       @ <p>To log off the system (and delete your login cookie)
   649    728       @  press the following button:<br />
   650    729       @ <input type="submit" name="out" value="Logout" /></p>
          730  +#endif
   651    731     }
   652    732     @ </form>
   653    733     if( g.perm.Password ){
   654    734       @ <hr />
          735  +#ifdef LANG_RU
          736  +    @ <p>Для смены своего пароля введите текущий пароль и два раза
          737  +    @ новый, после чего нажмите кнопку "Изменить пароль".</p>
          738  +    @ <form action="login" method="post">
          739  +    @ <table>
          740  +    @ <tr><td class="login_out_label">Старый пароль:</td>
          741  +    @ <td><input type="password" name="p" size="30" /></td></tr>
          742  +    @ <tr><td class="login_out_label">Новый пароль:</td>
          743  +    @ <td><input type="password" name="n1" size="30" /></td></tr>
          744  +    @ <tr><td class="login_out_label">Повторите новый пароль:</td>
          745  +    @ <td><input type="password" name="n2" size="30" /></td></tr>
          746  +    @ <tr><td></td>
          747  +    @ <td><input type="submit" value="Изменить пароль" /></td></tr>
          748  +#elif LANG_EN
   655    749       @ <p>To change your password, enter your old password and your
   656    750       @ new password twice below then press the "Change Password"
   657    751       @ button.</p>
   658    752       form_begin(0, "%R/login");
   659    753       @ <table>
   660    754       @ <tr><td class="login_out_label">Old Password:</td>
   661    755       @ <td><input type="password" name="p" size="30" /></td></tr>
   662    756       @ <tr><td class="login_out_label">New Password:</td>
   663    757       @ <td><input type="password" name="n1" size="30" /></td></tr>
   664    758       @ <tr><td class="login_out_label">Repeat New Password:</td>
   665    759       @ <td><input type="password" name="n2" size="30" /></td></tr>
   666    760       @ <tr><td></td>
   667    761       @ <td><input type="submit" value="Change Password" /></td></tr>
          762  +#endif
   668    763       @ </table>
   669    764       @ </form>
   670    765     }
   671    766     style_footer();
   672    767   }
   673    768   
   674    769   /*
................................................................................
  1143   1238   */
  1144   1239   void login_anonymous_available(void){
  1145   1240     if( !g.perm.Hyperlink &&
  1146   1241         db_exists("SELECT 1 FROM user"
  1147   1242                   " WHERE login='anonymous'"
  1148   1243                   "   AND cap LIKE '%%h%%'") ){
  1149   1244       const char *zUrl = PD("REQUEST_URI", "index");
         1245  +#ifdef LANG_RU
         1246  +    @ <p>Многие <span class="disabled">ссылки отключены.</span><br />
         1247  +    @ Используйте <a href="%s(g.zTop)/login?anon=1&amp;g=%T(zUrl)">анонимный вход</a>
         1248  +    @ для включения ссылок.</p>
         1249  +#elif LANG_EN
  1150   1250       @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br />
  1151   1251       @ Use <a href="%s(g.zTop)/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
  1152   1252       @ to enable hyperlinks.</p>
         1253  +#endif
  1153   1254     }
  1154   1255   }
  1155   1256   
  1156   1257   /*
  1157   1258   ** While rendering a form, call this routine to add the Anti-CSRF token
  1158   1259   ** as a hidden element of the form.
  1159   1260   */

Changes to src/main.mk.

     6      6   # This file is automatically generated.  Instead of editing this
     7      7   # file, edit "makemake.tcl" then run "tclsh makemake.tcl"
     8      8   # to regenerate this file.
     9      9   #
    10     10   # This file is included by primary Makefile.
    11     11   #
    12     12   
    13         -XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) -I$(OBJDIR)
           13  +XTCC = $(TCC) $(CFLAGS) -DFOSSIL_ENABLE_JSON -DLANG_RU -I. -I$(SRCDIR) -I$(OBJDIR)
    14     14   
    15     15   
    16     16   SRC = \
    17     17     $(SRCDIR)/add.c \
    18     18     $(SRCDIR)/allrepo.c \
    19     19     $(SRCDIR)/attach.c \
    20     20     $(SRCDIR)/bag.c \

Changes to src/manifest.c.

  1550   1550     if( !isNew ){
  1551   1551       for(i=0; i<pManifest->nField; i++){
  1552   1552         if( fossil_strcmp(pManifest->aField[i].zName, zStatusColumn)==0 ){
  1553   1553           zNewStatus = pManifest->aField[i].zValue;
  1554   1554         }
  1555   1555       }
  1556   1556       if( zNewStatus ){
         1557  +#ifdef LANG_RU
         1558  +      blob_appendf(&comment, "%h задача [%.10s]: <i>%h</i>",
         1559  +#elif LANG_EN
  1557   1560         blob_appendf(&comment, "%h ticket [%.10s]: <i>%h</i>",
         1561  +#endif
  1558   1562            zNewStatus, pManifest->zTicketUuid, zTitle
  1559   1563         );
  1560   1564         if( pManifest->nField>1 ){
         1565  +#ifdef LANG_RU
         1566  +        blob_appendf(&comment, " плюс еще изменений %d",
         1567  +#elif LANG_EN
  1561   1568           blob_appendf(&comment, " plus %d other change%s",
         1569  +#endif
  1562   1570             pManifest->nField-1, pManifest->nField==2 ? "" : "s");
  1563   1571         }
         1572  +#ifdef LANG_RU
         1573  +      blob_appendf(&brief, "%h задача [%.10s].",
         1574  +#elif LANG_EN
  1564   1575         blob_appendf(&brief, "%h ticket [%.10s].",
         1576  +#endif
  1565   1577                      zNewStatus, pManifest->zTicketUuid);
  1566   1578       }else{
  1567   1579         zNewStatus = db_text("unknown", 
  1568   1580            "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
  1569   1581            zStatusColumn, pManifest->zTicketUuid
  1570   1582         );
         1583  +#ifdef LANG_RU
         1584  +      blob_appendf(&comment, "Задача [%.10s] <i>%h</i> статус остался %h с "
         1585  +           "%d изменением",
         1586  +		   pManifest->zTicketUuid, zTitle, zNewStatus, pManifest->nField
         1587  +#elif LANG_EN
  1571   1588         blob_appendf(&comment, "Ticket [%.10s] <i>%h</i> status still %h with "
  1572   1589              "%d other change%s",
  1573         -           pManifest->zTicketUuid, zTitle, zNewStatus, pManifest->nField,
  1574         -           pManifest->nField==1 ? "" : "s"
         1590  +		   pManifest->zTicketUuid, zTitle, zNewStatus, pManifest->nField,
         1591  +		   pManifest->nField==1 ? "" : "s"
         1592  +#endif
  1575   1593         );
  1576   1594         free(zNewStatus);
         1595  +#ifdef LANG_RU
         1596  +      blob_appendf(&brief, "Задача [%.10s]: %d изменение",
         1597  +           pManifest->zTicketUuid, pManifest->nField
         1598  +#elif LANG_EN
  1577   1599         blob_appendf(&brief, "Ticket [%.10s]: %d change%s",
  1578   1600              pManifest->zTicketUuid, pManifest->nField,
  1579   1601              pManifest->nField==1 ? "" : "s"
         1602  +#endif
  1580   1603         );
  1581   1604       }
  1582   1605     }else{
         1606  +#ifdef LANG_RU
         1607  +    blob_appendf(&comment, "Новая задача [%.10s] <i>%h</i>.",
         1608  +#elif LANG_EN
  1583   1609       blob_appendf(&comment, "New ticket [%.10s] <i>%h</i>.",
         1610  +#endif
  1584   1611         pManifest->zTicketUuid, zTitle
  1585   1612       );
         1613  +#ifdef LANG_RU
         1614  +    blob_appendf(&brief, "Новая задача [%.10s].", pManifest->zTicketUuid);
         1615  +#elif LANG_EN
  1586   1616       blob_appendf(&brief, "New ticket [%.10s].", pManifest->zTicketUuid);
         1617  +#endif
  1587   1618     }
  1588   1619     free(zTitle);
  1589   1620     db_multi_exec(
  1590   1621       "REPLACE INTO event(type,tagid,mtime,objid,user,comment,brief)"
  1591   1622       "VALUES('t',%d,%.17g,%d,%Q,%Q,%Q)",
  1592   1623       tktTagId, pManifest->rDate, rid, pManifest->zUser,
  1593   1624       blob_str(&comment), blob_str(&brief)
................................................................................
  1769   1800         " ORDER BY mtime DESC",
  1770   1801         tagid, p->rDate
  1771   1802       );
  1772   1803       if( prior ){
  1773   1804         content_deltify(prior, rid, 0);
  1774   1805       }
  1775   1806       if( nWiki>0 ){
         1807  +#ifdef LANG_RU
         1808  +      zComment = mprintf("Правка страницы [%h]", p->zWikiTitle);
         1809  +#elif LANG_EN
  1776   1810         zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
         1811  +#endif
  1777   1812       }else{
         1813  +#ifdef LANG_RU
         1814  +      zComment = mprintf("Удаление страницы [%h]", p->zWikiTitle);
         1815  +#elif LANG_EN
  1778   1816         zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
         1817  +#endif
  1779   1818       }
  1780   1819       db_multi_exec(
  1781   1820         "REPLACE INTO event(type,mtime,objid,user,comment,"
  1782   1821         "                  bgcolor,euser,ecomment)"
  1783   1822         "VALUES('w',%.17g,%d,%Q,%Q,"
  1784   1823         "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
  1785   1824         "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
................................................................................
  1867   1906       );
  1868   1907       if( strlen(p->zAttachTarget)!=UUID_SIZE
  1869   1908        || !validate16(p->zAttachTarget, UUID_SIZE) 
  1870   1909       ){
  1871   1910         char *zComment;
  1872   1911         if( p->zAttachSrc && p->zAttachSrc[0] ){
  1873   1912           zComment = mprintf(
         1913  +#ifdef LANG_RU
         1914  +             "Добавить вложение [%R/artifact/%S|%h] к вики-странице [%h]",
         1915  +#elif LANG_EN
  1874   1916                "Add attachment [%R/artifact/%S|%h] to wiki page [%h]",
         1917  +#endif
  1875   1918                p->zAttachSrc, p->zAttachName, p->zAttachTarget);
  1876   1919         }else{
  1877         -        zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
         1920  +        zComment = mprintf(
         1921  +#ifdef LANG_RU
         1922  +          "Удален файл \"%h\" из страницы[%h]",
         1923  +#elif LANG_EN
         1924  +          "Delete attachment \"%h\" from wiki page [%h]",
         1925  +#endif
  1878   1926                p->zAttachName, p->zAttachTarget);
  1879   1927         }
  1880   1928         db_multi_exec(
  1881   1929           "REPLACE INTO event(type,mtime,objid,user,comment)"
  1882   1930           "VALUES('w',%.17g,%d,%Q,%Q)",
  1883   1931           p->rDate, rid, p->zUser, zComment
  1884   1932         );
  1885   1933         free(zComment);
  1886   1934       }else{
  1887   1935         char *zComment;
  1888   1936         if( p->zAttachSrc && p->zAttachSrc[0] ){
  1889   1937           zComment = mprintf(
         1938  +#ifdef LANG_RU
         1939  +             "Добавить вложение [%R/artifact/%S|%h] к задаче [%S]",
         1940  +#elif LANG_EN
  1890   1941                "Add attachment [%R/artifact/%S|%h] to ticket [%S]",
         1942  +#endif
  1891   1943                p->zAttachSrc, p->zAttachName, p->zAttachTarget);
  1892   1944         }else{
  1893         -        zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
         1945  +        zComment = mprintf(
         1946  +#ifdef LANG_RU
         1947  +          "Удален файл \"%h\" из задачи [%.10s]",
         1948  +#elif LANG_EN
         1949  +          "Delete attachment \"%h\" from ticket [%.10s]",
         1950  +#endif
  1894   1951                p->zAttachName, p->zAttachTarget);
  1895   1952         }
  1896   1953         db_multi_exec(
  1897   1954           "REPLACE INTO event(type,mtime,objid,user,comment)"
  1898   1955           "VALUES('t',%.17g,%d,%Q,%Q)",
  1899   1956           p->rDate, rid, p->zUser, zComment
  1900   1957         );

Changes to src/report.c.

    33     33     Blob ril;   /* Report Item List */
    34     34     Stmt q;
    35     35     int rn = 0;
    36     36     int cnt = 0;
    37     37   
    38     38     login_check_credentials();
    39     39     if( !g.perm.RdTkt && !g.perm.NewTkt ){ login_needed(); return; }
    40         -  style_header("Ticket Main Menu");
           40  +  style_header(
           41  +#ifdef LANG_RU
           42  +    "Задачи: основное меню"
           43  +#elif LANG_EN
           44  +    "Ticket Main Menu"
           45  +#endif
           46  +    );
    41     47     if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1);
    42     48     zScript = ticket_reportlist_code();
    43     49     if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1);
    44     50     
    45     51     blob_zero(&ril);
    46     52     ticket_init();
    47     53   
................................................................................
   994   1000     char *zTitle;
   995   1001     char *zOwner;
   996   1002     char *zClrKey;
   997   1003     int tabs;
   998   1004     Stmt q;
   999   1005     char *zErr1 = 0;
  1000   1006     char *zErr2 = 0;
         1007  +  Blob bSql = empty_blob;
  1001   1008   
  1002   1009     login_check_credentials();
  1003   1010     if( !g.perm.RdTkt ){ login_needed(); return; }
  1004   1011     rn = atoi(PD("rn","0"));
  1005   1012     if( rn==0 ){
  1006   1013       cgi_redirect("reportlist");
  1007   1014       return;
................................................................................
  1015   1022       return;
  1016   1023     }
  1017   1024     zTitle = db_column_malloc(&q, 0);
  1018   1025     zSql = db_column_malloc(&q, 1);
  1019   1026     zOwner = db_column_malloc(&q, 2);
  1020   1027     zClrKey = db_column_malloc(&q, 3);
  1021   1028     db_finalize(&q);
         1029  +
         1030  +
  1022   1031   
  1023   1032     if( P("order_by") ){
  1024   1033       /*
  1025   1034       ** If the user wants to do a column sort, wrap the query into a sub
  1026   1035       ** query and then sort the results. This is a whole lot easier than
  1027   1036       ** trying to insert an ORDER BY into the query itself, especially
  1028   1037       ** if the query is already ordered.
................................................................................
  1030   1039       int nField = atoi(P("order_by"));
  1031   1040       if( nField > 0 ){
  1032   1041         const char* zDir = PD("order_dir","");
  1033   1042         zDir = !strcmp("ASC",zDir) ? "ASC" : "DESC";
  1034   1043         zSql = mprintf("SELECT * FROM (%s) ORDER BY %d %s", zSql, nField, zDir);
  1035   1044       }
  1036   1045     }
         1046  +  Th_FossilInit(0, 0);
         1047  +  Th_Store("login", g.zLogin);
         1048  +  Th_Store("date", db_text(0, "SELECT datetime('now')"));
         1049  +  Th_RenderToBlob(zSql, &bSql);
         1050  +  zSql = bSql.aData;
  1037   1051   
  1038   1052     count = 0;
  1039   1053     if( !tabs ){
  1040   1054       struct GenerateHTML sState;
  1041   1055   
  1042   1056       db_multi_exec("PRAGMA empty_result_callbacks=ON");
  1043         -    style_submenu_element("Raw", "Raw", 
         1057  +    style_submenu_element(
         1058  +#ifdef LANG_RU
         1059  +      "Текстом", "Текстом", 
         1060  +#elif LANG_EN
         1061  +      "Raw", "Raw", 
         1062  +#endif
  1044   1063         "rptview?tablist=1&%h", PD("QUERY_STRING",""));
  1045   1064       if( g.perm.Admin 
  1046   1065          || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
  1047         -      style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn);
         1066  +      style_submenu_element(
         1067  +#ifdef LANG_RU
         1068  +        "Изменить", "Изменить",
         1069  +#elif LANG_EN
         1070  +        "Edit", "Edit",
         1071  +#endif
         1072  +         "rptedit?rn=%d", rn);
  1048   1073       }
  1049   1074       if( g.perm.TktFmt ){
  1050   1075         style_submenu_element("SQL", "SQL", "rptsql?rn=%d",rn);
  1051   1076       }
  1052   1077       if( g.perm.NewTkt ){
  1053         -      style_submenu_element("New Ticket", "Create a new ticket",
         1078  +      style_submenu_element(
         1079  +#ifdef LANG_RU
         1080  +        "Новая задача", "Создать новую задачу",
         1081  +#elif LANG_EN
         1082  +        "New Ticket", "Create a new ticket",
         1083  +#endif
  1054   1084           "%s/tktnew", g.zTop);
  1055   1085       }
  1056   1086       style_header(zTitle);
  1057   1087       output_color_key(zClrKey, 1, 
  1058   1088           "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");
  1059   1089       @ <table border="1" cellpadding="2" cellspacing="0" class="report"
  1060   1090       @  id="reportTable">

Changes to src/sqlite3.c.

 14081  14081   
 14082  14082     if( zDate[0]=='-' ){
 14083  14083       zDate++;
 14084  14084       neg = 1;
 14085  14085     }else{
 14086  14086       neg = 0;
 14087  14087     }
 14088         -  if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){
 14089         -    return 1;
        14088  +  if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 &&
        14089  +    getDigits(zDate,2,1,31,'.',&D,2,1,12,'.',&M,4,0,9999,0,&Y)!=3 ){
        14090  +      return 1;
 14090  14091     }
 14091  14092     zDate += 10;
 14092  14093     while( sqlite3Isspace(*zDate) || 'T'==*(u8*)zDate ){ zDate++; }
 14093  14094     if( parseHhMmSs(zDate, p)==0 ){
 14094  14095       /* We got the time */
 14095  14096     }else if( *zDate==0 ){
 14096  14097       p->validHMS = 0;
................................................................................
 14629  14630     int argc,
 14630  14631     sqlite3_value **argv
 14631  14632   ){
 14632  14633     DateTime x;
 14633  14634     if( isDate(context, argc, argv, &x)==0 ){
 14634  14635       char zBuf[100];
 14635  14636       computeYMD_HMS(&x);
 14636         -    sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d %02d:%02d:%02d",
 14637         -                     x.Y, x.M, x.D, x.h, x.m, (int)(x.s));
        14637  +    sqlite3_snprintf(sizeof(zBuf), zBuf, "%02d.%02d.%04d %02d:%02d:%02d",
        14638  +                     x.D, x.M, x.Y, x.h, x.m, (int)(x.s));
 14638  14639       sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
 14639  14640     }
 14640  14641   }
 14641  14642   
 14642  14643   /*
 14643  14644   **    time( TIMESTRING, MOD, MOD, ...)
 14644  14645   **

Changes to src/th_main.c.

   100    100       default: {
   101    101         sqlite3_snprintf(sizeof(zRc),zRc,"return code %d",rc);
   102    102       }
   103    103     }
   104    104     return zRc;
   105    105   }
   106    106   
          107  +static Blob* pOutBlob = NULL;
          108  +
   107    109   /*
   108    110   ** Send text to the appropriate output:  Either to the console
   109    111   ** or to the CGI reply buffer.  Escape all characters with special
   110    112   ** meaning to HTML if the encode parameter is true.
   111    113   */
   112    114   static void sendText(const char *z, int n, int encode){
   113    115     if( enableOutput && n ){
   114    116       if( n<0 ) n = strlen(z);
   115    117       if( encode ){
   116    118         z = htmlize(z, n);
   117    119         n = strlen(z);
   118    120       }
   119         -    if( g.cgiOutput ){
          121  +    if(pOutBlob)
          122  +      blob_append(pOutBlob, z, n);
          123  +    else if( g.cgiOutput ){
   120    124         cgi_append_content(z, n);
   121    125       }else{
   122    126         fwrite(z, 1, n, stdout);
   123    127         fflush(stdout);
   124    128       }
   125    129       if( encode ) free((char*)z);
   126    130     }
................................................................................
   916    920       zResult = (char*)Th_GetResult(g.interp, &n);
   917    921       sendError(zResult, n, 1);
   918    922     }else{
   919    923       sendText(z, i, 0);
   920    924     }
   921    925     return rc;
   922    926   }
          927  +
          928  +int Th_RenderToBlob(const char *z, Blob *pBlob){
          929  +  int result;
          930  +  pOutBlob = pBlob;
          931  +  result = Th_Render(z);
          932  +  pOutBlob = NULL;
          933  +  return result;
          934  +}
   923    935   
   924    936   /*
   925    937   ** COMMAND: test-th-render
   926    938   */
   927    939   void test_th_render(void){
   928    940     Blob in;
   929    941     if( g.argc<3 ){

Changes to src/timeline.c.

   818    818   */
   819    819   const char *timeline_query_for_www(void){
   820    820     static char *zBase = 0;
   821    821     static const char zBaseSql[] =
   822    822       @ SELECT
   823    823       @   blob.rid AS blobRid,
   824    824       @   uuid AS uuid,
   825         -    @   datetime(event.mtime,'localtime') AS timestamp,
          825  +    @   datetime(event.mtime, 'localtime') AS timestamp,
   826    826       @   coalesce(ecomment, comment) AS comment,
   827    827       @   coalesce(euser, user) AS user,
   828    828       @   blob.rid IN leaf AS leaf,
   829    829       @   bgcolor AS bgColor,
   830    830       @   event.type AS eventType,
   831    831       @   (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref
   832    832       @     WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
................................................................................
   919    919       zUuid
   920    920     );
   921    921     blob_zero(&out);
   922    922     while( db_step(&q)==SQLITE_ROW ){
   923    923       const char *zFN = db_column_text(&q, 0);
   924    924       blob_appendf(&out, "%s%z%h</a>", zSep,
   925    925             href("%R/finfo?name=%t", zFN), zFN);
          926  +#ifdef LANG_RU
          927  +    zSep = " или ";
          928  +#elif LANG_EN
   926    929       zSep = " or ";
          930  +#endif
   927    931     }
   928    932     db_finalize(&q);
   929    933     return blob_str(&out);
   930    934   }
   931    935   
          936  +#ifdef LANG_RU
          937  +const char* numberSuffix(int number, const char* s1, const char* s24, const char* s590){
          938  +	int lastDigit = number % 10;
          939  +	if(lastDigit == 1 && number != 11)
          940  +		return s1;
          941  +	if(lastDigit >= 2 && lastDigit <= 4 && (number < 12 || number > 14))
          942  +		return s24;
          943  +	return s590;
          944  +}
          945  +#elif LANG_EN
          946  +#define numberSuffix(n) (n==1?"":"s")
          947  +#endif
   932    948   
   933    949   /*
   934    950   ** WEBPAGE: timeline
   935    951   **
   936    952   ** Query parameters:
   937    953   **
   938    954   **    a=TIMEORTAG    after this event
................................................................................
  1043   1059         compute_uses_file("usesfile", ufid, 0);
  1044   1060         zType = "ci";
  1045   1061       }else{
  1046   1062         zUses = 0;
  1047   1063       }
  1048   1064     }
  1049   1065   
  1050         -  style_header("Timeline");
         1066  +  style_header(
         1067  +#ifdef LANG_RU
         1068  +    "События"
         1069  +#elif LANG_EN
         1070  +    "Timeline"
         1071  +#endif
         1072  +    );
  1051   1073     login_anonymous_available();
  1052   1074     timeline_temp_table();
  1053   1075     blob_zero(&sql);
  1054   1076     blob_zero(&desc);
  1055   1077     blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
  1056   1078     blob_append(&sql, timeline_query_for_www(), -1);
  1057   1079     if( P("fc")!=0 || P("detail")!=0 ){
................................................................................
  1080   1102       blob_append(&sql, " AND event.objid IN (0", -1);
  1081   1103       while( p ){
  1082   1104         blob_appendf(&sql, ",%d", p->rid);
  1083   1105         p = p->u.pTo;
  1084   1106       }
  1085   1107       blob_append(&sql, ")", -1);
  1086   1108       path_reset();
  1087         -    blob_append(&desc, "All nodes on the path from ", -1);
         1109  +    blob_append(&desc,
         1110  +#ifdef LANG_RU
         1111  +      "Все узлы с путём от ",
         1112  +#elif LANG_EN
         1113  +      "All nodes on the path from ",
         1114  +#endif
         1115  +      -1);
  1088   1116       blob_appendf(&desc, "%z%h</a>", href("%R/info/%h", zFrom), zFrom);
  1089         -    blob_append(&desc, " and ", -1);
         1117  +    blob_append(&desc,
         1118  +#ifdef LANG_RU
         1119  +      " до "
         1120  +#elif LANG_EN
         1121  +      " and "
         1122  +#endif
         1123  +      , -1);
  1090   1124       blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo);
  1091   1125       tmFlags |= TIMELINE_DISJOINT;
  1092   1126       db_multi_exec("%s", blob_str(&sql));
  1093   1127     }else if( (p_rid || d_rid) && g.perm.Read ){
  1094   1128       /* If p= or d= is present, ignore all other parameters other than n= */
  1095   1129       char *zUuid;
  1096   1130       int np, nd;
................................................................................
  1106   1140                            p_rid ? p_rid : d_rid);
  1107   1141       blob_appendf(&sql, " AND event.objid IN ok");
  1108   1142       nd = 0;
  1109   1143       if( d_rid ){
  1110   1144         compute_descendants(d_rid, nEntry+1);
  1111   1145         nd = db_int(0, "SELECT count(*)-1 FROM ok");
  1112   1146         if( nd>=0 ) db_multi_exec("%s", blob_str(&sql));
  1113         -      if( nd>0 ) blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
         1147  +      if( nd>0 )
         1148  +        blob_appendf(&desc,
         1149  +#ifdef LANG_RU
         1150  +          "%d потом%s", nd, numberSuffix(nd, "ок", "ка", "ков"));
         1151  +#elif LANG_EN
         1152  +          "%d descendant%s", nd, numberSuffix(nd));
         1153  +#endif
  1114   1154         if( useDividers ) timeline_add_dividers(0, d_rid);
  1115   1155         db_multi_exec("DELETE FROM ok");
  1116   1156       }
  1117   1157       if( p_rid ){
  1118   1158         compute_ancestors(p_rid, nEntry+1, 0);
  1119   1159         np = db_int(0, "SELECT count(*)-1 FROM ok");
  1120   1160         if( np>0 ){
  1121         -        if( nd>0 ) blob_appendf(&desc, " and ");
  1122         -        blob_appendf(&desc, "%d ancestors", np);
         1161  +        if( nd>0 ) blob_appendf(&desc,
         1162  +#ifdef LANG_RU
         1163  +          " и "
         1164  +#elif LANG_EN
         1165  +          " and "
         1166  +#endif
         1167  +          );
         1168  +        blob_appendf(&desc,
         1169  +#ifdef LANG_RU
         1170  +          "%d пред%s", np, numberSuffix(np, "ок", "ка", "ков"));
         1171  +#elif LANG_EN
         1172  +          "%d ancestor%s", numberSuffix(np));
         1173  +#endif
  1123   1174           db_multi_exec("%s", blob_str(&sql));
  1124   1175         }
  1125   1176         if( d_rid==0 && useDividers ) timeline_add_dividers(0, p_rid);
  1126   1177       }
  1127         -    blob_appendf(&desc, " of %z[%.10s]</a>",
  1128         -                   href("%R/info/%s", zUuid), zUuid);
         1178  +    blob_appendf(&desc,
         1179  +#ifdef LANG_RU
         1180  +      " %z[%.10s]</a>",
         1181  +#elif LANG_EN
         1182  +      " of %z[%.10s]</a>",
         1183  +#endif
         1184  +         href("%R/info/%s", zUuid), zUuid);
  1129   1185     }else if( f_rid && g.perm.Read ){
  1130   1186       /* If f= is present, ignore all other parameters other than n= */
  1131   1187       char *zUuid;
  1132   1188       db_multi_exec(
  1133   1189          "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
  1134   1190          "INSERT INTO ok VALUES(%d);"
  1135   1191          "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;"
  1136   1192          "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;",
  1137   1193          f_rid, f_rid, f_rid
  1138   1194       );
  1139   1195       blob_appendf(&sql, " AND event.objid IN ok");
  1140   1196       db_multi_exec("%s", blob_str(&sql));
  1141   1197       if( useDividers ) timeline_add_dividers(0, f_rid);
  1142         -    blob_appendf(&desc, "Parents and children of check-in ");
         1198  +    blob_appendf(&desc,
         1199  +#ifdef LANG_RU
         1200  +      "Предки и потомки фиксации "
         1201  +#elif LANG_EN
         1202  +      "Parents and children of check-in "
         1203  +#endif
         1204  +      );
  1143   1205       zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", f_rid);
  1144   1206       blob_appendf(&desc, "%z[%.10s]</a>", href("%R/info/%s", zUuid), zUuid);
  1145   1207       tmFlags |= TIMELINE_DISJOINT;
  1146   1208     }else{
  1147   1209       /* Otherwise, a timeline based on a span of time */
  1148   1210       int n;
  1149         -    const char *zEType = "timeline item";
         1211  +    const char *zEType =
         1212  +#ifdef LANG_RU
         1213  +      "событие";
         1214  +#elif LANG_EN
         1215  +      "timeline item";
         1216  +#endif
  1150   1217       char *zDate;
  1151   1218       char *zNEntry = mprintf("%d", nEntry);
  1152   1219       url_add_parameter(&url, "n", zNEntry);
  1153   1220       if( zUses ){
  1154   1221         blob_appendf(&sql, " AND event.objid IN usesfile ");
  1155   1222       }
  1156   1223       if( tagid>0 ){
................................................................................
  1279   1346       }else{
  1280   1347         blob_appendf(&sql, " ORDER BY event.mtime DESC");
  1281   1348       }
  1282   1349       blob_appendf(&sql, " LIMIT %d", nEntry);
  1283   1350       db_multi_exec("%s", blob_str(&sql));
  1284   1351   
  1285   1352       n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
  1286         -    if( zAfter==0 && zBefore==0 && zCirca==0 ){
  1287         -      blob_appendf(&desc, "%d most recent %ss", n, zEType);
  1288         -    }else{
  1289         -      blob_appendf(&desc, "%d %ss", n, zEType);
         1353  +    {
         1354  +#ifdef LANG_RU
         1355  +      const char* rZEtype;
         1356  +      const char* rSuffix;
         1357  +      const char* rLastTitleSuffix = numberSuffix(n, "яя", "ие", "их");
         1358  +      switch(zEType[0]){
         1359  +          case 'c': /*check-in*/
         1360  +            rZEtype = "фиксаци";
         1361  +            rSuffix = numberSuffix(n, "я", "и", "й");
         1362  +            break;
         1363  +          case 'w': /*wiki-edit*/
         1364  +            rZEtype = "вики-прав";
         1365  +            rSuffix = numberSuffix(n, "ка", "ки", "ок");
         1366  +            break;
         1367  +          case 't': /*tiket-change*/
         1368  +            rZEtype = "задач";
         1369  +            rSuffix = numberSuffix(n, "а", "и", "");
         1370  +            break;
         1371  +          case 'e': /*events*/
         1372  +            rZEtype = "новост";
         1373  +            rSuffix = numberSuffix(n, "ь", "и", "ей");
         1374  +            break;
         1375  +          default:
         1376  +            rZEtype = "событи";
         1377  +            rSuffix = numberSuffix(n, "е", "я", "й");
         1378  +            rLastTitleSuffix = numberSuffix(n, "ее", "их", "их");
         1379  +      }
         1380  +#endif
         1381  +      if( zAfter==0 && zBefore==0 && zCirca==0 ){
         1382  + #ifdef LANG_RU
         1383  +        blob_appendf(&desc, "%d последн%s %s%s", n, rLastTitleSuffix, rZEtype, rSuffix);
         1384  +#elif LANG_EN
         1385  +        blob_appendf(&desc, "%d most recent %ss", n, zEType);
         1386  +#endif
         1387  +      }else{
         1388  +#ifdef LANG_RU
         1389  +        blob_appendf(&desc, "%d %s%s", n, rZEtype, rSuffix);
         1390  +#elif LANG_EN
         1391  +        blob_appendf(&desc, "%d %ss", n, zEType);
         1392  +#endif
         1393  +      }
  1290   1394       }
  1291   1395       if( zUses ){
  1292   1396         char *zFilenames = names_of_file(zUses);
  1293   1397         blob_appendf(&desc, " using file %s version %z%S</a>", zFilenames,
  1294   1398                      href("%R/artifact/%S",zUses), zUses);
  1295   1399         tmFlags |= TIMELINE_DISJOINT;
  1296   1400       }
  1297   1401       if( zUser ){
         1402  +#ifdef LANG_RU
         1403  +      blob_appendf(&desc, " пользователя %h", zUser);
         1404  +#elif LANG_EN
  1298   1405         blob_appendf(&desc, " by user %h", zUser);
         1406  +#endif
  1299   1407         tmFlags |= TIMELINE_DISJOINT;
  1300   1408       }
  1301   1409       if( zTagName ){
         1410  +#ifdef LANG_RU
         1411  +      blob_appendf(&desc, " с тэгом \"%h\"", zTagName);
         1412  +#elif LANG_EN
  1302   1413         blob_appendf(&desc, " tagged with \"%h\"", zTagName);
         1414  +#endif
  1303   1415         tmFlags |= TIMELINE_DISJOINT;
  1304   1416       }else if( zBrName ){
         1417  +#ifdef LANG_RU
         1418  +      blob_appendf(&desc, " ветки \"%h\"", zBrName);
         1419  +#elif LANG_EN
  1305   1420         blob_appendf(&desc, " related to \"%h\"", zBrName);
         1421  +#endif
  1306   1422         tmFlags |= TIMELINE_DISJOINT;
  1307   1423       }
  1308   1424       if( rAfter>0.0 ){
  1309   1425         if( rBefore>0.0 ){
  1310         -        blob_appendf(&desc, " occurring between %h and %h.<br>",
  1311         -                     zAfter, zBefore);
         1426  +        blob_appendf(&desc,
         1427  +#ifdef LANG_RU
         1428  +			" между %h and %h.<br>"
         1429  +#elif LANG_EN
         1430  +			" occurring between %h and %h.<br>"
         1431  +#endif
         1432  +			, zAfter, zBefore);
  1312   1433         }else{
  1313         -        blob_appendf(&desc, " occurring on or after %h.<br />", zAfter);
         1434  +        blob_appendf(&desc,
         1435  +#ifdef LANG_RU
         1436  +			" после %h.<br />"
         1437  +#elif LANG_EN
         1438  +			" occurring on or after %h.<br />"
         1439  +#endif
         1440  +			, zAfter);
  1314   1441         }
  1315   1442       }else if( rBefore>0.0 ){
         1443  +#ifdef LANG_RU
         1444  +      blob_appendf(&desc, " до %h.<br />", zBefore);
         1445  +#elif LANG_EN
  1316   1446         blob_appendf(&desc, " occurring on or before %h.<br />", zBefore);
         1447  +#endif
  1317   1448       }else if( rCirca>0.0 ){
         1449  +#ifdef LANG_RU
         1450  +      blob_appendf(&desc, " около %h.<br />", zCirca);
         1451  +#elif LANG_EN
  1318   1452         blob_appendf(&desc, " occurring around %h.<br />", zCirca);
         1453  +#endif
  1319   1454       }
  1320   1455       if( zSearch ){
         1456  +#ifdef LANG_RU
         1457  +      blob_appendf(&desc, " похожие на \"%h\"", zSearch);
         1458  +#elif LANG_EN
  1321   1459         blob_appendf(&desc, " matching \"%h\"", zSearch);
         1460  +#endif
  1322   1461       }
  1323   1462       if( g.perm.Hyperlink ){
  1324   1463         if( zAfter || n==nEntry ){
  1325   1464           zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
         1465  +#ifdef LANG_RU
         1466  +        timeline_submenu(&url, "Ранее", "b", zDate, "a");
         1467  +#elif LANG_EN
  1326   1468           timeline_submenu(&url, "Older", "b", zDate, "a");
         1469  +#endif
  1327   1470           free(zDate);
  1328   1471         }
  1329   1472         if( zBefore || (zAfter && n==nEntry) ){
  1330   1473           zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
         1474  +#ifdef LANG_RU
         1475  +        timeline_submenu(&url, "Новее", "a", zDate, "b");
         1476  +#elif LANG_EN
  1331   1477           timeline_submenu(&url, "Newer", "a", zDate, "b");
         1478  +#endif
  1332   1479           free(zDate);
  1333   1480         }else if( tagid==0 ){
  1334   1481           if( zType[0]!='a' ){
         1482  +#ifdef LANG_RU
         1483  +          timeline_submenu(&url, "Все типы", "y", "all", 0);
         1484  +#elif LANG_EN
  1335   1485             timeline_submenu(&url, "All Types", "y", "all", 0);
         1486  +#endif
  1336   1487           }
  1337   1488           if( zType[0]!='w' && g.perm.RdWiki ){
         1489  +#ifdef LANG_RU
         1490  +          timeline_submenu(&url, "Только вики-правки", "y", "w", 0);
         1491  +#elif LANG_EN
  1338   1492             timeline_submenu(&url, "Wiki Only", "y", "w", 0);
         1493  +#endif
  1339   1494           }
  1340   1495           if( zType[0]!='c' && g.perm.Read ){
         1496  +#ifdef LANG_RU
         1497  +          timeline_submenu(&url, "Только фиксации", "y", "ci", 0);
         1498  +#elif LANG_EN
  1341   1499             timeline_submenu(&url, "Checkins Only", "y", "ci", 0);
         1500  +#endif
  1342   1501           }
  1343   1502           if( zType[0]!='t' && g.perm.RdTkt ){
         1503  +#ifdef LANG_RU
         1504  +          timeline_submenu(&url, "Только задачи", "y", "t", 0);
         1505  +#elif LANG_EN
  1344   1506             timeline_submenu(&url, "Tickets Only", "y", "t", 0);
         1507  +#endif
  1345   1508           }
  1346   1509           if( zType[0]!='e' && g.perm.RdWiki ){
         1510  +#ifdef LANG_RU
         1511  +          timeline_submenu(&url, "Только новости", "y", "e", 0);
         1512  +#elif LANG_EN
  1347   1513             timeline_submenu(&url, "Events Only", "y", "e", 0);
         1514  +#endif
  1348   1515           }
  1349   1516           if( zType[0]!='g' && g.perm.Read ){
  1350   1517             timeline_submenu(&url, "Tags Only", "y", "g", 0);
  1351   1518           }
  1352   1519         }
  1353   1520         if( nEntry>20 ){
         1521  +#ifdef LANG_RU
         1522  +        timeline_submenu(&url, "20 событий", "n", "20", 0);
         1523  +#elif LANG_EN
  1354   1524           timeline_submenu(&url, "20 Entries", "n", "20", 0);
         1525  +#endif
  1355   1526         }
  1356   1527         if( nEntry<200 ){
         1528  +#ifdef LANG_RU
         1529  +        timeline_submenu(&url, "200 событий", "n", "200", 0);
         1530  +#elif LANG_EN
  1357   1531           timeline_submenu(&url, "200 Entries", "n", "200", 0);
         1532  +#endif
  1358   1533         }
  1359   1534         if( zType[0]=='a' || zType[0]=='c' ){
  1360   1535           if( tmFlags & TIMELINE_FCHANGES ){
         1536  +#ifdef LANG_RU
         1537  +          timeline_submenu(&url, "Не показывать файлы", "fc", 0, 0);
         1538  +#elif LANG_EN
  1361   1539             timeline_submenu(&url, "Hide Files", "fc", 0, 0);
         1540  +#endif
  1362   1541           }else{
         1542  +#ifdef LANG_RU
         1543  +          timeline_submenu(&url, "Показывать файлы", "fc", "", 0);
         1544  +#elif LANG_EN
  1363   1545             timeline_submenu(&url, "Show Files", "fc", "", 0);
         1546  +#endif
  1364   1547           }
  1365   1548         }
  1366   1549       }
  1367   1550     }
  1368   1551     if( P("showsql") ){
  1369   1552       @ <blockquote>%h(blob_str(&sql))</blockquote>
  1370   1553     }

Changes to src/tkt.c.

   420    420     const char *zScript;
   421    421     char *zFullName;
   422    422     const char *zUuid = PD("name","");
   423    423   
   424    424     login_check_credentials();
   425    425     if( !g.perm.RdTkt ){ login_needed(); return; }
   426    426     if( g.perm.WrTkt || g.perm.ApndTkt ){
   427         -    style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
          427  +    style_submenu_element(
          428  +#ifdef LANG_RU
          429  +      "Правка", "Изменить задачу",
          430  +#elif LANG_EN
          431  +      "Edit", "Edit The Ticket",
          432  +#endif
          433  +      "%s/tktedit?name=%T",
   428    434           g.zTop, PD("name",""));
   429    435     }
   430    436     if( g.perm.Hyperlink ){
   431         -    style_submenu_element("History", "History Of This Ticket", 
          437  +    style_submenu_element(
          438  +#ifdef LANG_RU
          439  +      "История", "Исория задачи", 
          440  +#elif LANG_EN
          441  +      "History", "History Of This Ticket", 
          442  +#endif
   432    443           "%s/tkthistory/%T", g.zTop, zUuid);
   433         -    style_submenu_element("Timeline", "Timeline Of This Ticket", 
          444  +    style_submenu_element(
          445  +#ifdef LANG_RU
          446  +      "События", "События по этой задаче", 
          447  +#elif LANG_EN
          448  +      "Timeline", "Timeline Of This Ticket", 
          449  +#endif
   434    450           "%s/tkttimeline/%T", g.zTop, zUuid);
   435         -    style_submenu_element("Check-ins", "Check-ins Of This Ticket", 
          451  +    style_submenu_element(
          452  +#ifdef LANG_RU
          453  +      "Фиксации", "Фиксации по этой задаче", 
          454  +#elif LANG_EN
          455  +      "Check-ins", "Check-ins Of This Ticket", 
          456  +#endif
   436    457           "%s/tkttimeline/%T?y=ci", g.zTop, zUuid);
   437    458     }
   438    459     if( g.perm.NewTkt ){
   439         -    style_submenu_element("New Ticket", "Create a new ticket",
          460  +    style_submenu_element(
          461  +#ifdef LANG_RU
          462  +      "Новая задача", "Создать новую задачу",
          463  +#elif LANG_EN
          464  +      "New Ticket", "Create a new ticket",
          465  +#endif
   440    466           "%s/tktnew", g.zTop);
   441    467     }
   442    468     if( g.perm.ApndTkt && g.perm.Attach ){
   443         -    style_submenu_element("Attach", "Add An Attachment",
          469  +    style_submenu_element(
          470  +#ifdef LANG_RU
          471  +      "Добавить файл", "Добавить файл к задаче",
          472  +#elif LANG_EN
          473  +      "Attach", "Add An Attachment",
          474  +#endif
   444    475           "%s/attachadd?tkt=%T&from=%s/tktview/%t",
   445    476           g.zTop, zUuid, g.zTop, zUuid);
   446    477     }
   447    478     if( P("plaintext") ){
          479  +#ifdef LANG_RU
          480  +    style_submenu_element("Форматировано", "Форматировано", "%R/tktview/%S", zUuid);
          481  +#elif LANG_EN
   448    482       style_submenu_element("Formatted", "Formatted", "%R/tktview/%S", zUuid);
          483  +#endif
   449    484     }else{
          485  +#ifdef LANG_RU
          486  +    style_submenu_element("Текстом", "Текстом",
          487  +#elif LANG_EN
   450    488       style_submenu_element("Plaintext", "Plaintext",
          489  +#endif
   451    490                             "%R/tktview/%S?plaintext", zUuid);
   452    491     }
   453         -  style_header("View Ticket");
          492  +  style_header(
          493  +#ifdef LANG_RU
          494  +	  "Просмотр задачи"
          495  +#elif LANG_EN
          496  +	  "View Ticket"
          497  +#endif
          498  +	  );
   454    499     if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
   455    500     ticket_init();
   456    501     initializeVariablesFromCGI();
   457    502     getAllTicketFields();
   458    503     initializeVariablesFromDb();
   459    504     zScript = ticket_viewpage_code();
   460    505     if( P("showfields")!=0 ) showAllFields();
................................................................................
   462    507     Th_Render(zScript);
   463    508     if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
   464    509   
   465    510     zFullName = db_text(0, 
   466    511          "SELECT tkt_uuid FROM ticket"
   467    512          " WHERE tkt_uuid GLOB '%q*'", zUuid);
   468    513     if( zFullName ){
          514  +#ifdef LANG_RU
          515  +    attachment_list(zFullName, "<hr /><h2>Вложения:</h2><ul>");
          516  +#elif LANG_EN
   469    517       attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>");
          518  +#endif
   470    519     }
   471    520    
   472    521     style_footer();
   473    522   }
   474    523   
   475    524   /*
   476    525   ** TH command:   append_field FIELD STRING
................................................................................
   558    607     const char *zUuid;
   559    608     int i;
   560    609     int nJ = 0;
   561    610     Blob tktchng, cksum;
   562    611   
   563    612     login_verify_csrf_secret();
   564    613     if( !captcha_is_correct() ){
          614  +#ifdef LANG_RU
          615  +    @ <p class="generalError">Ошибка: неверный код.</p>
          616  +#elif LANG_EN
   565    617       @ <p class="generalError">Error: Incorrect security code.</p>
          618  +#endif
   566    619       return TH_OK;
   567    620     }
   568    621     zUuid = (const char *)pUuid;
   569    622     blob_zero(&tktchng);
   570    623     zDate = date_in_standard_format("now");
   571    624     blob_appendf(&tktchng, "D %s\n", zDate);
   572    625     free(zDate);
................................................................................
   579    632     }
   580    633     for(i=0; i<nField; i++){
   581    634       const char *zValue;
   582    635       int nValue;
   583    636       if( aField[i].zAppend ) continue;
   584    637       zValue = Th_Fetch(aField[i].zName, &nValue);
   585    638       if( zValue ){
          639  +      const char* zTemp = wiki_sanify(zValue);
          640  +      if(zTemp != zValue)
          641  +      {
          642  +        zValue = zTemp;
          643  +        nValue = strlen(zValue);
          644  +      }
   586    645         while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; }
   587    646         if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0)
   588    647          || memcmp(zValue, aField[i].zValue, nValue)!=0
   589    648          || strlen(aField[i].zValue)!=nValue
   590    649         ){
   591    650           if( memcmp(aField[i].zName, "private_", 8)==0 ){
   592    651             zValue = db_conceal(zValue, nValue);
................................................................................
   650    709     char *zNewUuid = 0;
   651    710   
   652    711     login_check_credentials();
   653    712     if( !g.perm.NewTkt ){ login_needed(); return; }
   654    713     if( P("cancel") ){
   655    714       cgi_redirect("home");
   656    715     }
   657         -  style_header("New Ticket");
          716  +  style_header(
          717  +#ifdef LANG_RU
          718  +    "Новая задача"
          719  +#elif LANG_EN
          720  +    "New Ticket"
          721  +#endif
          722  +    );
   658    723     if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
   659    724     ticket_init();
   660    725     initializeVariablesFromCGI();
   661    726     getAllTicketFields();
   662    727     initializeVariablesFromDb();
   663    728     if( g.zPath[0]=='d' ) showAllFields();
   664    729     form_begin(0, "%R/%s", g.zPath);
................................................................................
   701    766   
   702    767     login_check_credentials();
   703    768     if( !g.perm.ApndTkt && !g.perm.WrTkt ){ login_needed(); return; }
   704    769     zName = P("name");
   705    770     if( P("cancel") ){
   706    771       cgi_redirectf("tktview?name=%T", zName);
   707    772     }
   708         -  style_header("Edit Ticket");
          773  +  style_header(
          774  +#ifdef LANG_RU
          775  +    "Изменение задачи"
          776  +#elif LANG_EN
          777  +    "Edit Ticket"
          778  +#endif
          779  +    );
   709    780     if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE
   710    781             || !validate16(zName,nName) ){
   711         -    @ <span class="tktError">Not a valid ticket id: \"%h(zName)\"</span>
          782  +#ifdef LANG_RU
          783  +          @ <span class="tktError">Неверный ID задачи: \"%h(zName)\"</span>
          784  +#elif LANG_EN
          785  +          @ <span class="tktError">Not a valid ticket id: \"%h(zName)\"</span>
          786  +#endif
   712    787       style_footer();
   713    788       return;
   714    789     }
   715    790     nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'",
   716    791                   zName);
   717    792     if( nRec==0 ){
          793  +#ifdef LANG_RU
          794  +    @ <span class="tktError">Не найдена задача: \"%h(zName)\"</span>
          795  +#elif LANG_EN
   718    796       @ <span class="tktError">No such ticket: \"%h(zName)\"</span>
          797  +#endif
   719    798       style_footer();
   720    799       return;
   721    800     }
   722    801     if( nRec>1 ){
          802  +#ifdef LANG_RU
          803  +    @ <span class="tktError">Несколько задач (%d(nRec)) начинаются с:
          804  +#elif LANG_EN
   723    805       @ <span class="tktError">%d(nRec) tickets begin with:
          806  +#endif
   724    807       @ \"%h(zName)\"</span>
   725    808       style_footer();
   726    809       return;
   727    810     }
   728    811     if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
   729    812     ticket_init();
   730    813     getAllTicketFields();
................................................................................
   801    884     const char *zType;
   802    885   
   803    886     login_check_credentials();
   804    887     if( !g.perm.Hyperlink || !g.perm.RdTkt ){ login_needed(); return; }
   805    888     zUuid = PD("name","");
   806    889     zType = PD("y","a");
   807    890     if( zType[0]!='c' ){
   808         -    style_submenu_element("Check-ins", "Check-ins",
          891  +    style_submenu_element(
          892  +#ifdef LANG_RU
          893  +      "Фиксации", "Фиксации",
          894  +#elif LANG_EN
          895  +      "Check-ins", "Check-ins",
          896  +#endif
   809    897          "%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid);
   810    898     }else{
   811         -    style_submenu_element("Timeline", "Timeline",
          899  +    style_submenu_element(
          900  +#ifdef LANG_RU
          901  +      "События", "События",
          902  +#elif LANG_EN
          903  +      "Timeline", "Timeline",
          904  +#endif
   812    905          "%s/tkttimeline?name=%T", g.zTop, zUuid);
   813    906     }
   814         -  style_submenu_element("History", "History",
          907  +  style_submenu_element(
          908  +#ifdef LANG_RU
          909  +    "История", "История",
          910  +#elif LANG_EN
          911  +    "History", "History",
          912  +#endif
   815    913       "%s/tkthistory/%s", g.zTop, zUuid);
   816         -  style_submenu_element("Status", "Status",
          914  +  style_submenu_element(
          915  +#ifdef LANG_RU
          916  +    "Статус", "Статус",
          917  +#elif LANG_EN
          918  +    "Status", "Status",
          919  +#endif
   817    920       "%s/info/%s", g.zTop, zUuid);
   818    921     if( zType[0]=='c' ){
   819         -    zTitle = mprintf("Check-Ins Associated With Ticket %h", zUuid);
          922  +    zTitle = mprintf(
          923  +#ifdef LANG_RU
          924  +      "Фиксации, связанные с задачей %h",
          925  +#elif LANG_EN
          926  +      "Check-Ins Associated With Ticket %h",
          927  +#endif
          928  +      zUuid);
   820    929     }else{
   821         -    zTitle = mprintf("Timeline Of Ticket %h", zUuid);
          930  +    zTitle = mprintf(
          931  +#ifdef LANG_RU
          932  +      "События, связанные с задачей %h",
          933  +#elif LANG_EN
          934  +      "Timeline Of Ticket %h",
          935  +#endif
          936  +      zUuid);
   822    937     }
   823    938     style_header(zTitle);
   824    939     free(zTitle);
   825    940   
   826    941     sqlite3_snprintf(6, zGlobPattern, "%s", zUuid);
   827    942     canonical16(zGlobPattern, strlen(zGlobPattern));
   828    943     tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
   829    944     if( tagid==0 ){
          945  +#ifdef LANG_RU
          946  +    @ Задача %h(zUuid) не найдена.
          947  +#elif LANG_EN
   830    948       @ No such ticket: %h(zUuid)
          949  +#endif
   831    950       style_footer();
   832    951       return;
   833    952     }
   834    953     zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d",
   835    954                            tagid);
   836    955     if( zType[0]=='c' ){
   837    956       zSQL = mprintf(
................................................................................
   874    993     const char *zUuid;
   875    994     int tagid;
   876    995     int nChng = 0;
   877    996   
   878    997     login_check_credentials();
   879    998     if( !g.perm.Hyperlink || !g.perm.RdTkt ){ login_needed(); return; }
   880    999     zUuid = PD("name","");
   881         -  zTitle = mprintf("History Of Ticket %h", zUuid);
   882         -  style_submenu_element("Status", "Status",
         1000  +  zTitle = mprintf(
         1001  +#ifdef LANG_RU
         1002  +    "История задачи %h",
         1003  +#elif LANG_EN
         1004  +    "History Of Ticket %h",
         1005  +#endif
         1006  +    zUuid);
         1007  +  style_submenu_element(
         1008  +#ifdef LANG_RU
         1009  +    "Статус", "Статус",
         1010  +#elif LANG_EN
         1011  +    "Status", "Status",
         1012  +#endif
   883   1013       "%s/info/%s", g.zTop, zUuid);
   884         -  style_submenu_element("Check-ins", "Check-ins",
         1014  +#ifdef LANG_RU
         1015  +    style_submenu_element("Фиксации", "Фиксации",
         1016  +#elif LANG_EN
         1017  +    style_submenu_element("Check-ins", "Check-ins",
         1018  +#endif
   885   1019       "%s/tkttimeline?name=%s&y=ci", g.zTop, zUuid);
   886         -  style_submenu_element("Timeline", "Timeline",
         1020  +#ifdef LANG_RU
         1021  +    style_submenu_element("События", "События",
         1022  +#elif LANG_EN
         1023  +    style_submenu_element("Timeline", "Timeline",
         1024  +#endif
   887   1025       "%s/tkttimeline?name=%s", g.zTop, zUuid);
   888   1026     if( P("plaintext")!=0 ){
   889   1027       style_submenu_element("Formatted", "Formatted",
   890   1028                             "%R/tkthistory/%S", zUuid);
   891   1029     }else{
   892   1030       style_submenu_element("Plaintext", "Plaintext",
   893   1031                             "%R/tkthistory/%S?plaintext", zUuid);
   894   1032     }
   895   1033     style_header(zTitle);
   896   1034     free(zTitle);
   897   1035   
   898   1036     tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
   899   1037     if( tagid==0 ){
         1038  +#ifdef LANG_RU
         1039  +    @ Задача %h(zUuid) не найдена
         1040  +#elif LANG_EN
   900   1041       @ No such ticket: %h(zUuid)
         1042  +#endif
   901   1043       style_footer();
   902   1044       return;
   903   1045     }
   904   1046     db_prepare(&q,
   905   1047       "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
   906   1048       "  FROM event, blob"
   907   1049       " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
................................................................................
   928   1070       }
   929   1071       nChng++;
   930   1072       if( zFile!=0 ){
   931   1073         const char *zSrc = db_column_text(&q, 3);
   932   1074         const char *zUser = db_column_text(&q, 5);
   933   1075         if( zSrc==0 || zSrc[0]==0 ){
   934   1076           @ 
         1077  +#ifdef LANG_RU
         1078  +        @ <li>Удалено вложение "%h(zFile)"
         1079  +#elif LANG_EN
   935   1080           @ <li><p>Delete attachment "%h(zFile)"
         1081  +#endif
   936   1082         }else{
   937   1083           @ 
         1084  +#ifdef LANG_RU
         1085  +        @ <li><p>Добавлено вложение
         1086  +#elif LANG_EN
   938   1087           @ <li><p>Add attachment
   939         -        @ "%z(href("%R/artifact/%S",zSrc))%h(zFile)</a>"
         1088  +#endif
         1089  +		@ "%z(href("%R/artifact/%S",zSrc))%h(zFile)</a>"
   940   1090         }
   941   1091         @ [%z(href("%R/artifact/%T",zChngUuid))%s(zShort)</a>]
         1092  +#ifdef LANG_RU
         1093  +      @ (rid %d(rid)) пользователем
         1094  +      hyperlink_to_user(zUser,zDate," ");
         1095  +#elif LANG_EN
   942   1096         @ (rid %d(rid)) by
   943   1097         hyperlink_to_user(zUser,zDate," on");
         1098  +#endif
   944   1099         hyperlink_to_date(zDate, ".</p>");
   945   1100       }else{
   946   1101         pTicket = manifest_get(rid, CFTYPE_TICKET);
   947   1102         if( pTicket ){
   948   1103           @
         1104  +#ifdef LANG_RU
         1105  +        @ <li>Задача изменена
         1106  +        @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
         1107  +        @ (rid %d(rid)) пользователем
         1108  +        hyperlink_to_user(pTicket->zUser,zDate," ");
         1109  +#elif LANG_EN
   949   1110           @ <li><p>Ticket change
   950   1111           @ [%z(href("%R/artifact/%T",zChngUuid))%s(zShort)</a>]
   951   1112           @ (rid %d(rid)) by
   952   1113           hyperlink_to_user(pTicket->zUser,zDate," on");
         1114  +#endif
   953   1115           hyperlink_to_date(zDate, ":");
   954   1116           @ </p>
   955   1117           ticket_output_change_artifact(pTicket, "a");
   956   1118         }
   957   1119         manifest_destroy(pTicket);
   958   1120       }
   959   1121     }

Changes to src/url.c.

   378    378   
   379    379   /*
   380    380   ** Prompt the user for the password for g.urlUser.  Store the result
   381    381   ** in g.urlPasswd.
   382    382   */
   383    383   void url_prompt_for_password(void){
   384    384     if( isatty(fileno(stdin)) ){
   385         -    char *zPrompt = mprintf("\rpassword for %s: ", g.urlUser);
          385  +    char *zPrompt = mprintf(
          386  +#ifdef LANG_RU
          387  +		"\rПароль для %s: ",
          388  +#elif LANG_EN
          389  +		"\rpassword for %s: ",
          390  +#endif
          391  +		g.urlUser);
   386    392       Blob x;
   387    393       prompt_for_password(zPrompt, &x, 0);
   388    394       free(zPrompt);
   389    395       g.urlPasswd = mprintf("%b", &x);
   390    396       blob_reset(&x);
   391    397     }else{
   392         -    fossil_fatal("missing or incorrect password for user \"%s\"",
          398  +    fossil_fatal(
          399  +#ifdef LANG_RU
          400  +		"Отсутствует или неверен пароль для пользователя \"%s\"",
          401  +#elif LANG_EN
          402  +		"missing or incorrect password for user \"%s\"",
          403  +#endif
   393    404                    g.urlUser);
   394    405     }
   395    406   }
   396    407   
   397    408   /* Preemptively prompt for a password if a username is given in the
   398    409   ** URL but no password.
   399    410   */

Changes to src/user.c.

    47     47   #endif
    48     48   /*
    49     49   ** getpass for Windows
    50     50   */
    51     51   static char *getpass(const char *prompt){
    52     52     static char pwd[64];
    53     53     size_t i;
    54         -
    55         -  fputs(prompt,stderr);
           54  +  fossil_puts(prompt,1);
    56     55     fflush(stderr);
    57     56     for(i=0; i<sizeof(pwd)-1; ++i){
    58     57       pwd[i] = _getch();
    59     58       if(pwd[i]=='\r' || pwd[i]=='\n'){
    60     59         break;
    61     60       }
    62     61       /* BS or DEL */
................................................................................
   113    112     Blob secondTry;
   114    113     blob_zero(pPassphrase);
   115    114     blob_zero(&secondTry);
   116    115     while(1){
   117    116       prompt_for_passphrase(zPrompt, pPassphrase);
   118    117       if( verify==0 ) break;
   119    118       if( verify==1 && blob_size(pPassphrase)==0 ) break;
   120         -    prompt_for_passphrase("Retype new password: ", &secondTry);
          119  +    prompt_for_passphrase(
          120  +#ifdef LANG_RU
          121  +		"��������� ����� ������: ",
          122  +#elif LANG_EN
          123  +		"Retype new password: ",
          124  +#endif
          125  +		&secondTry);
   121    126       if( blob_compare(pPassphrase, &secondTry) ){
   122         -      fossil_print("Passphrases do not match.  Try again...\n");
          127  +      fossil_print(
          128  +#ifdef LANG_RU
          129  +		  "������ �� ���������. ���������� ��� ���...\n"
          130  +#elif LANG_EN
          131  +		  "Passphrases do not match.  Try again...\n"
          132  +#endif
          133  +		  );
   123    134       }else{
   124    135         break;
   125    136       }
   126    137     }
   127    138     blob_reset(&secondTry);
   128    139   }
   129    140   

Changes to src/wiki.c.

    45     45     return 1;
    46     46   }
    47     47   
    48     48   /*
    49     49   ** Output rules for well-formed wiki pages
    50     50   */
    51     51   static void well_formed_wiki_name_rules(void){
           52  +#ifdef LANG_RU
           53  +  @ <ul>
           54  +    @ <li>Не должно начинаться или заканчиваться пробелом.</li>
           55  +    @ <li> Не должно содержать управляющих символов, включая табуляцию 
           56  +    @      и перевод строки.</li>
           57  +    @ <li> Не должно быть двух и более пробелов подряд.</li>
           58  +    @ <li> Длина от 3 до 100 символов.</li>
           59  +    @ </ul>
           60  +#elif LANG_EN
    52     61     @ <ul>
    53     62     @ <li> Must not begin or end with a space.</li>
    54     63     @ <li> Must not contain any control characters, including tab or
    55     64     @      newline.</li>
    56     65     @ <li> Must not have two or more spaces in a row internally.</li>
    57     66     @ <li> Must be between 3 and 100 characters in length.</li>
    58     67     @ </ul>
           68  +#endif
    59     69   }
    60     70   
    61     71   /*
    62     72   ** Check a wiki name.  If it is not well-formed, then issue an error
    63     73   ** and return true.  If it is well-formed, return false.
    64     74   */
    65     75   static int check_name(const char *z){
    66     76     if( !wiki_name_is_wellformed((const unsigned char *)z) ){
           77  +#ifdef LANG_RU
           78  +    style_header("Ошибка в названии страницы");
           79  +    @ "<span class="wikiError">%h(z)</span>" не может быть названием страницы.
           80  +    @ Правила для названий страниц:
           81  +#elif LANG_EN
    67     82       style_header("Wiki Page Name Error");
    68     83       @ The wiki name "<span class="wikiError">%h(z)</span>" is not well-formed.
    69     84       @ Rules for wiki page names:
           85  +#endif
    70     86       well_formed_wiki_name_rules();
    71     87       style_footer();
    72     88       return 1;
    73     89     }
    74     90     return 0;
    75     91   }
    76     92   
................................................................................
    99    115       login_check_credentials();
   100    116       g.zExtra = zPageName;
   101    117       cgi_set_parameter_nocopy("name", g.zExtra);
   102    118       g.isHome = 1;
   103    119       wiki_page();
   104    120       return;
   105    121     }
          122  +#ifdef LANG_RU
          123  +  style_header("Домашняя страница");
          124  +  @ <p>Это фиктивная домашняя страница проекта.
          125  +  @ Для заполнения этой страницы сначала перейдите в
          126  +  @ <a href="%s(g.zTop)/setup_config">setup/config</a>
          127  +  @ и задайте имя проекта. Затем создайте страницу с
          128  +  @ таким же названием. Содержимое созданной страницы
          129  +  @ будет отображаться вместо этого сообщения.</p>
          130  +#elif LANG_EN
   106    131     style_header("Home");
   107    132     @ <p>This is a stub home-page for the project.
   108    133     @ To fill in this page, first go to
   109    134     @ %z(href("%R/setup_config"))setup/config</a>
   110    135     @ and establish a "Project Name".  Then create a
   111    136     @ wiki page with that name.  The content of that wiki page
   112    137     @ will be displayed in place of this message.</p>
          138  +#endif
   113    139     style_footer();
   114    140   }
   115    141   
   116    142   /*
   117    143   ** Return true if the given pagename is the name of the sandbox
   118    144   */
   119    145   static int is_sandbox(const char *zPagename){
................................................................................
   129    155     char *zTag;
   130    156     int rid = 0;
   131    157     int isSandbox;
   132    158     char *zUuid;
   133    159     Blob wiki;
   134    160     Manifest *pWiki = 0;
   135    161     const char *zPageName;
          162  +#ifdef LANG_RU
          163  +  char *zBody = mprintf("%s","<i>Пустая страница</i>");
          164  +#elif LANG_EN
   136    165     char *zBody = mprintf("%s","<i>Empty Page</i>");
   137         -
          166  +#endif
   138    167     login_check_credentials();
   139    168     if( !g.perm.RdWiki ){ login_needed(); return; }
   140    169     zPageName = P("name");
   141    170     if( zPageName==0 ){
          171  +#ifdef LANG_RU
          172  +    style_header("Вики");
          173  +    @ <ul>
          174  +#elif LANG_EN
   142    175       style_header("Wiki");
   143    176       @ <ul>
          177  +#endif
   144    178       { char *zHomePageName = db_get("project-name",0);
   145    179         if( zHomePageName ){
   146    180           @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
          181  +#ifdef LANG_RU
          182  +        @      %h(zHomePageName)</a> - домашняя страница.</li>
          183  +#elif LANG_EN
   147    184           @      %h(zHomePageName)</a> wiki home page.</li>
          185  +#endif
   148    186         }
   149    187       }
          188  +#ifdef LANG_RU
          189  +    @ <li> %z(href("%R/timeline?y=w"))Последние правки</a></li>
          190  +    @ <li> %z(href("%R/wiki_rules"))Правила форматирования</a></li>
          191  +    @ <li> Используйте %z(href("%R/wiki?name=Sandbox"))песочницу</a> для экспериментов.</li>
          192  +#elif LANG_EN
   150    193       @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
   151    194       @ <li> %z(href("%R/wiki_rules"))Formatting rules</a> for wiki.</li>
   152    195       @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
   153    196       @      to experiment.</li>
          197  +#endif
   154    198       if( g.perm.NewWiki ){
          199  +#ifdef LANG_RU
          200  +      @ <li> Создать %z(href("%R/wikinew"))новую страницу</a>.</li>
          201  +#elif LANG_EN
   155    202         @ <li>  Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
          203  +#endif
   156    204         if( g.perm.Write ){
          205  +#ifdef LANG_RU
          206  +        @ <li> Создать %z(href("%R/eventedit"))новое событие</a>.</li>
          207  +#elif LANG_EN
   157    208           @ <li>   Create a %z(href("%R/eventedit"))new event</a>.</li>
          209  +#endif
   158    210         }
   159    211       }
          212  +#ifdef LANG_RU
          213  +    @ <li> %z(href("%R/wcontent"))Все страницы</a>
          214  +    @      на сервере.</li>
          215  +    @ <li> <form method="get" action="%s(g.zTop)/wfind"><div>
          216  +    @     Поиск по названиям: <input type="text" name="title"/>
          217  +    @  &nbsp; <input type="submit" value = "Искать" /></div></form>
          218  +#elif LANG_EN
   160    219       @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
   161    220       @      available on this server.</li>
   162    221       if( g.perm.ModWiki ){
   163    222         @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
   164    223       }
   165    224       @ <li>
   166    225       form_begin(0, "%R/wfind");
   167    226       @  <div>Search wiki titles: <input type="text" name="title"/>
   168    227       @  &nbsp; <input type="submit" /></div></form>
          228  +#endif
   169    229       @ </li>
   170    230       @ </ul>
   171    231       style_footer();
   172    232       return;
   173    233     }
   174    234     if( check_name(zPageName) ) return;
   175    235     isSandbox = is_sandbox(zPageName);
................................................................................
   195    255                    "%R/wdiff?name=%T&a=%d", zPageName, rid);
   196    256         zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   197    257         style_submenu_element("Details", "Details",
   198    258                      "%R/info/%S", zUuid);
   199    259       }
   200    260       if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){
   201    261         if( db_get_boolean("wysiwyg-wiki", 0) ){
   202         -        style_submenu_element("Edit", "Edit Wiki Page",
   203         -             "%s/wikiedit?name=%T&wysiwyg=1",
          262  +        style_submenu_element(
          263  +#ifdef LANG_RU
          264  +			"Правка", "Редактировать вики-страницу"
          265  +#elif LANG_EN
          266  +			"Edit", "Edit Wiki Page"
          267  +#endif
          268  +			, "%s/wikiedit?name=%T&wysiwyg=1",
   204    269                g.zTop, zPageName);
   205    270         }else{
   206         -        style_submenu_element("Edit", "Edit Wiki Page",
   207         -             "%s/wikiedit?name=%T",
          271  +        style_submenu_element(
          272  +#ifdef LANG_RU
          273  +			"Правка", "Редактировать вики-страницу"
          274  +#elif LANG_EN
          275  +			"Edit", "Edit Wiki Page"
          276  +#endif
          277  +			, "%s/wikiedit?name=%T",
   208    278                g.zTop, zPageName);
   209    279         }
   210    280       }
   211    281       if( rid && g.perm.ApndWiki && g.perm.Attach ){
   212         -      style_submenu_element("Attach", "Add An Attachment",
          282  +      style_submenu_element(
          283  +#ifdef LANG_RU
          284  +        "Добавить файл", "Добавить файл к странице",
          285  +#elif LANG_EN
          286  +        "Attach", "Add An Attachment",
          287  +#endif
   213    288              "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
   214    289              g.zTop, zPageName, g.zTop, zPageName);
   215    290       }
   216    291       if( rid && g.perm.ApndWiki ){
   217         -      style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
          292  +      style_submenu_element(
          293  +#ifdef LANG_RU
          294  +        "Комментировать", "Добавить комментарий к странице", 
          295  +#elif LANG_EN
          296  +        "Append", "Add A Comment", 
          297  +#endif
          298  +        "%s/wikiappend?name=%T",
   218    299              g.zTop, zPageName);
   219    300       }
   220    301       if( g.perm.Hyperlink ){
   221         -      style_submenu_element("History", "History", "%s/whistory?name=%T",
          302  +      style_submenu_element(
          303  +#ifdef LANG_RU
          304  +        "История", "Показать историю правок",
          305  +#elif LANG_EN
          306  +        "History", "History",
          307  +#endif
          308  +        "%s/whistory?name=%T",
   222    309              g.zTop, zPageName);
   223    310       }
   224    311     }
   225    312     style_set_current_page("%s?name=%T", g.zPath, zPageName);
   226    313     style_header(zPageName);
   227    314     blob_init(&wiki, zBody, -1);
   228    315     wiki_convert(&wiki, 0, 0);
................................................................................
   451    538     char *zDate;
   452    539     const char *zUser;
   453    540     const char *zRemark;
   454    541     char *zId;
   455    542   
   456    543     zDate = db_text(0, "SELECT datetime('now')");
   457    544     zId = db_text(0, "SELECT lower(hex(randomblob(8)))");
   458         -  blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h", 
          545  +  blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>%s %h", 
   459    546       zId, zDate, g.zLogin);
   460    547     free(zDate);
   461    548     zUser = PD("u",g.zLogin);
   462    549     if( zUser[0] && fossil_strcmp(zUser,g.zLogin) ){
   463         -    blob_appendf(p, " (claiming to be %h)", zUser);
          550  +    blob_appendf(p, ", представившись как %h,", zUser);
   464    551     }
   465         -  zRemark = PD("r","");
   466         -  blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId);
          552  +  zRemark = wiki_sanify(PD("r",""));
          553  +  blob_appendf(p, " добавил:</i><br />\n%s</div id=\"%s\">", zRemark, zId);
   467    554   }
   468    555   
   469    556   /*
   470    557   ** WEBPAGE: wikiappend
   471    558   ** URL: /wikiappend?name=PAGENAME
   472    559   */
   473    560   void wikiappend_page(void){
................................................................................
   704    791   */
   705    792   void wcontent_page(void){
   706    793     Stmt q;
   707    794     int showAll = P("all")!=0;
   708    795   
   709    796     login_check_credentials();
   710    797     if( !g.perm.RdWiki ){ login_needed(); return; }
          798  +#ifdef LANG_RU
          799  +  style_header("Доступные вики-стариницы");
          800  +#elif LANG_EN
   711    801     style_header("Available Wiki Pages");
          802  +#endif
          803  +  
   712    804     if( showAll ){
          805  +#ifdef LANG_RU
          806  +    style_submenu_element("Активные", "Только активные страницы", "%s/wcontent", g.zTop);
          807  +#elif LANG_EN
   713    808       style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
          809  +#endif
   714    810     }else{
          811  +#ifdef LANG_RU
          812  +    style_submenu_element("Все", "Все страницы", "%s/wcontent?all=1", g.zTop);
          813  +#elif LANG_EN
   715    814       style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
          815  +#endif
   716    816     }
          817  +  
   717    818     @ <ul>
   718    819     wiki_prepare_page_list(&q);
   719    820     while( db_step(&q)==SQLITE_ROW ){
   720    821       const char *zName = db_column_text(&q, 0);
   721    822       int size = db_column_int(&q, 1);
   722    823       if( size>0 ){
   723    824         @ <li>%z(href("%R/wiki?name=%T",zName))%h(zName)</a></li>

Changes to src/wikiformat.c.

   232    232   #define MUTYPE_TD          0x0100   /* <td> or <th> */
   233    233   #define MUTYPE_SPECIAL     0x0200   /* <nowiki> or <verbatim> */
   234    234   #define MUTYPE_HYPERLINK   0x0400   /* <a> */
   235    235   
   236    236   /*
   237    237   ** These markup types must have an end tag.
   238    238   */
   239         -#define MUTYPE_STACK  (MUTYPE_BLOCK | MUTYPE_FONT | MUTYPE_LIST | MUTYPE_TABLE)
          239  +#define MUTYPE_STACK  (MUTYPE_BLOCK | MUTYPE_LIST | MUTYPE_TABLE)
   240    240   
   241    241   /*
   242    242   ** This markup types are allowed for "inline" text.
   243    243   */
   244    244   #define MUTYPE_INLINE (MUTYPE_FONT | MUTYPE_HYPERLINK)
   245    245   
   246    246   static const struct AllowedMarkup {
................................................................................
   379    379   #define TOKEN_NEWLINE       5  /* A single "\n" */
   380    380   #define TOKEN_BUL_LI        6  /*  "  *  " */
   381    381   #define TOKEN_NUM_LI        7  /*  "  #  " */
   382    382   #define TOKEN_ENUM          8  /*  "  \(?\d+[.)]?  " */
   383    383   #define TOKEN_INDENT        9  /*  "   " */
   384    384   #define TOKEN_RAW           10 /* Output exactly (used when wiki-use-html==1) */
   385    385   #define TOKEN_TEXT          11 /* None of the above */
          386  +#define TOKEN_SWHTML        12 /* switch use html */
   386    387   
   387    388   /*
   388    389   ** State flags.  Save the lower 16 bits for the WIKI_* flags.
   389    390   */
   390    391   #define AT_NEWLINE          0x0010000  /* At start of a line */
   391    392   #define AT_PARAGRAPH        0x0020000  /* At start of a paragraph */
   392    393   #define ALLOW_WIKI          0x0040000  /* Allow wiki markup */
................................................................................
   630    631   ** characters in that token.  Write the token type into *pTokenType.
   631    632   */
   632    633   static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){
   633    634     int n;
   634    635     if( z[0]=='<' ){
   635    636       n = markupLength(z);
   636    637       if( n>0 ){
   637         -      *pTokenType = TOKEN_MARKUP;
          638  +      if(n == 3 && z[1] == 'x')
          639  +        *pTokenType = TOKEN_SWHTML;
          640  +      else
          641  +        *pTokenType = TOKEN_MARKUP;
   638    642         return n;
   639         -    }else{
          643  +    } else {
   640    644         *pTokenType = TOKEN_CHARACTER;
   641    645         return 1;
   642    646       }
   643    647     }
          648  +  
   644    649     if( z[0]=='&' && (p->inVerbatim || !isElement(z)) ){
   645    650       *pTokenType = TOKEN_CHARACTER;
   646    651       return 1;
   647    652     }
   648    653     if( (p->state & ALLOW_WIKI)!=0 ){
   649    654       if( z[0]=='\n' ){
   650    655         n = paragraphBreakLength(z);
................................................................................
   699    704   ** characters in that token. Write the token type into *pTokenType.
   700    705   */
   701    706   static int nextRawToken(const char *z, Renderer *p, int *pTokenType){
   702    707     int n;
   703    708     if( z[0]=='[' && (n = linkLength(z))>0 ){
   704    709       *pTokenType = TOKEN_LINK;
   705    710       return n;
          711  +  }
          712  +  if( z[0]=='<' && z[1]=='x' && z[2] == '>') {
          713  +    *pTokenType = TOKEN_SWHTML;
          714  +    return 3;
   706    715     }
   707    716     *pTokenType = TOKEN_RAW;
   708    717     return 1 + textLength(z+1, p->state);
   709    718   }
   710    719   
   711    720   /*
   712    721   ** A single markup is parsed into an instance of the following
................................................................................
   819    828   static void renderMarkup(Blob *pOut, ParsedMarkup *p){
   820    829     int i;
   821    830     if( p->endTag ){
   822    831       blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
   823    832     }else{
   824    833       blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
   825    834       for(i=0; i<p->nAttr; i++){
          835  +	  if(p->aAttr[i].iACode==ATTR_HREF && 0 == fossil_strnicmp(p->aAttr[i].zValue, "JavaScript:", sizeof("JavaScript:") - 1))
          836  +		  continue;
   826    837         blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
   827    838         if( p->aAttr[i].zValue ){
   828    839           const char *zVal = p->aAttr[i].zValue;
   829    840           if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){
   830    841             blob_appendf(pOut, "=\"%s%s\"", g.zTop, zVal);
   831    842           }else{
   832    843             blob_appendf(pOut, "=\"%s\"", zVal);
................................................................................
   924    935   ** output its end tag if it is not a </div> tag.
   925    936   */
   926    937   static void popStack(Renderer *p){
   927    938     if( p->nStack ){
   928    939       int iCode;
   929    940       p->nStack--;
   930    941       iCode = p->aStack[p->nStack].iCode;
   931         -    if( iCode!=MARKUP_DIV && p->pOut ){
          942  +    if( /*iCode!=MARKUP_DIV &&*/ p->pOut ){
   932    943         blob_appendf(p->pOut, "</%s>", aMarkup[iCode].zName);
   933    944       }
   934    945     }
   935    946   }
   936    947   
   937    948   /*
   938    949   ** Push a new markup value onto the stack.  Enlarge the stack
................................................................................
  1271   1282     int tokenType;
  1272   1283     ParsedMarkup markup;
  1273   1284     int n;
  1274   1285     int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
  1275   1286     int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
  1276   1287     int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
  1277   1288     char *zOrig = z;
         1289  +  int forceUseHtml = 0;
  1278   1290   
  1279   1291     /* Make sure the attribute constants and names still align
  1280   1292     ** following changes in the attribute list. */
  1281   1293     assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );
  1282   1294   
  1283   1295     while( z[0] ){
  1284         -    if( wikiHtmlOnly ){
         1296  +    if( wikiHtmlOnly || forceUseHtml){
  1285   1297         n = nextRawToken(z, p, &tokenType);
  1286   1298       }else{
  1287   1299         n = nextWikiToken(z, p, &tokenType);
  1288   1300       }
  1289   1301       p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
  1290   1302       switch( tokenType ){
         1303  +	    case TOKEN_SWHTML: {
         1304  +		    forceUseHtml = !forceUseHtml;
         1305  +        break;
         1306  +	    }
  1291   1307         case TOKEN_PARAGRAPH: {
  1292   1308           if( inlineOnly ){
  1293   1309             /* blob_append(p->pOut, " &para; ", -1); */
  1294   1310             blob_append(p->pOut, " &nbsp;&nbsp; ", -1);
  1295   1311           }else{
  1296   1312             if( p->wikiList ){
  1297   1313               popStackToTag(p, p->wikiList);
................................................................................
  1519   1535           */
  1520   1536           if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
  1521   1537             /* Do nothing */
  1522   1538           }else
  1523   1539   
  1524   1540           /* Generate end-tags */
  1525   1541           if( markup.endTag ){
  1526         -          popStackToTag(p, markup.iCode);
         1542  +          if(markup.iType==MUTYPE_FONT)
         1543  +            renderMarkup(p->pOut, &markup);
         1544  +          else
         1545  +            popStackToTag(p, markup.iCode);
  1527   1546           }else
  1528   1547   
  1529   1548           /* Push <div> markup onto the stack together with the id=ID attribute.
  1530   1549           */
  1531   1550           if( markup.iCode==MARKUP_DIV ){
  1532   1551             pushStackWithId(p, markup.iCode, markupId(&markup),
  1533   1552                             (p->state & ALLOW_WIKI)!=0);
         1553  +          renderMarkup(p->pOut, &markup);
  1534   1554           }else
  1535   1555   
  1536   1556           /* Enter <verbatim> processing.  With verbatim enabled, all other
  1537   1557           ** markup other than the corresponding end-tag with the same ID is
  1538   1558           ** ignored.
  1539   1559           */
  1540   1560           if( markup.iCode==MARKUP_VERBATIM ){
................................................................................
  1591   1611               popStackToTag(p, markup.iCode);
  1592   1612               startAutoParagraph(p);
  1593   1613               renderMarkup(p->pOut, &markup);
  1594   1614               pushStack(p, markup.iCode);
  1595   1615             }
  1596   1616           }else
  1597   1617           {
  1598         -          if( markup.iType==MUTYPE_FONT ){
         1618  +          /*if( markup.iType==MUTYPE_FONT ){
  1599   1619               startAutoParagraph(p);
  1600         -          }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
         1620  +          }else*/
         1621  +          if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
  1601   1622               p->wantAutoParagraph = 0;
  1602   1623             }
  1603   1624             if(   markup.iCode==MARKUP_HR
  1604   1625                || markup.iCode==MARKUP_H1
  1605   1626                || markup.iCode==MARKUP_H2
  1606   1627                || markup.iCode==MARKUP_H3
  1607   1628                || markup.iCode==MARKUP_H4
................................................................................
  1905   1926           break;
  1906   1927         }
  1907   1928       }
  1908   1929       z += n;
  1909   1930     }
  1910   1931     free(renderer.aStack);
  1911   1932   }
         1933  +/*
         1934  +** Test text before it add to wiki. Allow use of <x> only for developers or wikieditors.
         1935  +*/
         1936  +const char* wiki_sanify(const char *pSrc){
         1937  +  static Blob tempStore = BLOB_INITIALIZER;
         1938  +  int hasReplaces = 0;
         1939  +  const char* z = pSrc;
         1940  +  Renderer r;
         1941  +  
         1942  +  blob_reset(&tempStore);
         1943  +
         1944  +  if(g.perm.Admin || g.perm.NewWiki || g.perm.Write || g.perm.WrWiki || g.perm.Setup)
         1945  +    return pSrc;
         1946  +  r.state = 1;
         1947  +  while(*z)
         1948  +  {
         1949  +    int tokenType;
         1950  +    int n = nextRawToken(z, &r, &tokenType);
         1951  +    if(tokenType == TOKEN_SWHTML) {
         1952  +      if(!hasReplaces) {
         1953  +        blob_append(&tempStore, pSrc, z - pSrc);
         1954  +        hasReplaces = 1;
         1955  +      }
         1956  +      blob_append(&tempStore, "&lt;x&gt;", 9);
         1957  +    }
         1958  +    else if(hasReplaces)
         1959  +      blob_append(&tempStore, z, n);
         1960  +    z+=n;
         1961  +  }
         1962  +  return hasReplaces ? blob_str(&tempStore) : pSrc;
         1963  +}
  1912   1964   
  1913   1965   /*
  1914   1966   ** Get the next HTML token.
  1915   1967   **
  1916   1968   ** z points to the start of a token.  Return the number of
  1917   1969   ** characters in that token.
  1918   1970   */