Index: skins/bootstrap/header.txt ================================================================== --- skins/bootstrap/header.txt +++ skins/bootstrap/header.txt @@ -4,11 +4,11 @@ $<project_name>: $<title> - + + @ } /* ** All extra JS files to load. */ @@ -1063,16 +1077,60 @@ ** WEBPAGE: style.css ** ** Return the style sheet. */ void page_style_css(void){ - Blob css; + Blob css = empty_blob; int i; int isInit = 0; + const char * zPage = P("name") + /* FIXME: our reliance on P("name") will merge-conflict with the + [style-css-revamp] branch, at which time we need to adopt its + version of this function. The problem with doing that *now* is + that it requires new functionality in cgi.c and url.c which was + added for that purposes, which we don't dare merge into here + until that branch is vetted and merged. That branch's whole + approach to the ordering of CSS is different than this one + (which is based on the current trunk). */; cgi_set_content_type("text/css"); - blob_init(&css,skin_get("css"),-1); + if(zPage!=0 && zPage[0]!=0 + && strlen(zPage)<30/*marginal safety measure vs malicious input*/){ + /* Check for page-specific CSS. The placement of this CSS is kinda + ** tricky. It "very probably needs" to come before any + ** skin-supplied CSS, but if it does then a system-level skin + ** which does silly things like set *all* textareas to the same + ** 32px tall (Ardoise) can effectively ruin page-specific + ** layout. If the page-specific CSS is emitted after the skin, + ** then the page-specific CSS will potentially override any user + ** edits made to the skin, leaving the user with no way to + ** override them except to import a separate CSS file from their + ** custom skin, after this one. Thus the page CSS needs to come + ** first, but it also needs "unusually specific" + ** (i.e. strongly-binding) CSS classes for any style which "needs" + ** to override the *default* skin CSS, but which is nonetheless + ** overridable by client-side edits by using CSS selectors of + ** equal or higher specificity. + ** + ** The alternative to this approach is that we pack all + ** page-specific CSS into default_css.txt, which can explode it + ** tremendously. e.g. /fileedit itself includes 330-ish lines of + ** CSS. + */ + int nLen = 0; + char * zPageCss = mprintf("style.%s.css",zPage); + const char * zBuiltin = (const char *)builtin_file(zPageCss, &nLen); + fossil_free(zPageCss); + if(nLen>0){ + blob_append(&css, zBuiltin, nLen); + } + } + if(blob_size(&css)>0){ + blob_append(&css,skin_get("css"),-1); + }else{ + blob_init(&css,skin_get("css"),-1); + } /* add special missing definitions */ for(i=1; cssDefaultList[i].elementClass; i++){ char *z = blob_str(&css); if( !containsSelector(z, cssDefaultList[i].elementClass) ){ @@ -1259,10 +1317,11 @@ @ anonymous-adds = %s(find_anon_capabilities(zCap))
} @ g.zRepositoryName = %h(g.zRepositoryName)
@ load_average() = %f(load_average())
@ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))
+ @ fossil_exe_id() = %h(fossil_exe_id())
@
P("HTTP_USER_AGENT"); cgi_print_all(showAll, 0); if( showAll && blob_size(&g.httpHeader)>0 ){ @
@@ -1297,42 +1356,65 @@ #if INTERFACE # define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);} #endif /* -** Outputs a labeled checkbox element. zFieldName is the form element -** name. zLabel is the label for the checkbox. zValue is the optional -** value for the checkbox. zTip is an optional tooltip, which gets set -** as the "title" attribute of the outermost element. If isChecked is -** true, the checkbox gets the "checked" attribute set, else it is -** not. +** Returns a pseudo-random input field ID, for use in associating an +** ID-less input field with a label. The memory is owned by the +** caller. +*/ +static char * style_next_input_id(){ + static int inputID = 0; + ++inputID; + return mprintf("input-id-%d", inputID); +} + +/* +** Outputs a labeled checkbox element. zWrapperId is an optional ID +** value for the containing element (see below). zFieldName is the +** form element name. zLabel is the label for the checkbox. zValue is +** the optional value for the checkbox. zTip is an optional tooltip, +** which gets set as the "title" attribute of the outermost +** element. If isChecked is true, the checkbox gets the "checked" +** attribute set, else it is not. ** ** Resulting structure: ** -**
+** ** -** {{zLabel}} -**
+** +** ** -** zFieldName, zLabel, and zValue are required. zTip is optional. +** zLabel, and zValue are required. zFieldName, zWrapperId, and zTip +** are may be NULL or empty. ** ** Be sure that the input-with-label CSS class is defined sensibly, in ** particular, having its display:inline-block is useful for alignment ** purposes. */ -void style_labeled_checkbox(const char *zFieldName, const char * zLabel, - const char * zValue, const char * zTip, - int isChecked){ - CX("
", - zFieldName, + if(zWrapperId && *zWrapperId){ + CX(" id='%s'",zWrapperId); + } + CX(">", zValue ? zValue : "", isChecked ? " checked" : ""); - CX("%h
", zLabel); + CX("", zLabelID, zLabel); + fossil_free(zLabelID); } /* ** Outputs a SELECT list from a compile-time list of integers. ** The vargs must be a list of (const char *, int) pairs, terminated @@ -1347,47 +1429,58 @@ ** ** Note that the pairs are not in (int, const char *) order because ** there is no well-known integer value which we can definitively use ** as a list terminator. ** -** zFieldName is the value of the form element's name attribute. +** zWrapperId is an optional ID value for the containing element (see +** below). +** +** zFieldName is the value of the form element's name attribute. Note +** that fossil prefers underscores over '-' for separators in form +** element names. ** ** zLabel is an optional string to use as a "label" for the element ** (see below). ** ** zTooltip is an optional value for the SELECT's title attribute. ** ** The structure of the emitted HTML is: ** -**
-** {{zLabel}} -** -**
+** +** +** +** ** ** Example: ** -** style_select_list_int("my_field", "Grapes", +** style_select_list_int("my-grapes", "my_grapes", "Grapes", ** "Select the number of grapes", ** atoi(PD("my_field","0")), ** "", 1, "2", 2, "Three", 3, ** NULL); ** */ -void style_select_list_int(const char *zFieldName, const char * zLabel, +void style_select_list_int(const char * zWrapperId, + const char *zFieldName, const char * zLabel, const char * zToolTip, int selectedVal, ... ){ + char * zLabelID = style_next_input_id(); va_list vargs; + va_start(vargs,selectedVal); - CX("
"); if(zLabel && *zLabel){ - CX("%h", zLabel); + CX("", zLabelID, zLabel); } - CX("",zFieldName, zLabelID); while(1){ const char * zOption = va_arg(vargs,char *); int v; if(NULL==zOption){ break; @@ -1401,33 +1494,260 @@ CX("%d",v); } CX("\n"); } CX("\n"); + CX("\n"); + va_end(vargs); + fossil_free(zLabelID); +} + +/* +** The C-string counterpart of style_select_list_int(), this variant +** differs only in that its variadic arguments are C-strings in pairs +** of (optionLabel, optionValue). If a given optionLabel is an empty +** string, the corresponding optionValue is used as its label. If any +** given value matches zSelectedVal, that option gets preselected. If +** no options match zSelectedVal then the first entry is selected by +** default. +** +** Any of (zWrapperId, zTooltip, zSelectedVal) may be NULL or empty. +** +** Example: +** +** style_select_list_str("my-grapes", "my_grapes", "Grapes", +** "Select the number of grapes", +** P("my_field"), +** "1", "One", "2", "Two", "", "3", +** NULL); +*/ +void style_select_list_str(const char * zWrapperId, + const char *zFieldName, const char * zLabel, + const char * zToolTip, char const * zSelectedVal, + ... ){ + char * zLabelID = style_next_input_id(); + va_list vargs; + + va_start(vargs,zSelectedVal); + if(!zSelectedVal){ + zSelectedVal = __FILE__/*some string we'll never match*/; + } + CX(""); if(zLabel && *zLabel){ - CX("
\n"); + CX("", zLabelID, zLabel); + } + CX("\n"); + CX("\n"); va_end(vargs); + fossil_free(zLabelID); } + +/* +** The first time this is called, it emits code to install and +** bootstrap the window.fossil object, using the built-in file +** fossil.bootstrap.js (not to be confused with bootstrap.js). +** +** Subsequent calls are no-ops. +** +** If passed a true value, it emits the contents directly to the page +** output, else it emits a script tag with a src=builtin/... to load +** the script. It always outputs a small pre-bootstrap element in its +** own script tag to initialize parts which need C-runtime-level +** information, before loading the main fossil.bootstrap.js either +** inline or via a +** +** zSrc is always assumed to be a repository-relative path without +** a leading slash, and has %R/ prepended to it. +** +** Meaning that no follow-up call to pass a non-0 first argument +** to close the tag. zSrc is ignored if the first argument is not +** 0. +** */ -void style_emit_script_tag(int phase){ - static int once = 0; - if(0==phase){ - CX("\n", zSrc); + }else{ + CX("\n"); } } + +/* +** Emits a script tag which uses content from a builtin script file. +** +** If asInline is true, it is emitted directly as an opening tag, the +** content of the zName builtin file, and a closing tag. +** +** If it is false, a script tag loading it via +** src=builtin/{{zName}}?cache=XYZ is emitted, where XYZ is a +** build-time-dependent cache-buster value. +*/ +void style_emit_script_builtin(int asInline, char const * zName){ + if(asInline){ + style_emit_script_tag(0,0); + CX("%s", builtin_text(zName)); + style_emit_script_tag(1,0); + }else{ + char * zFullName = mprintf("builtin/%s",zName); + const char * zHash = fossil_exe_id(); + CX("\n", + zFullName, zHash); + fossil_free(zFullName); + } +} + +/* +** The first time this is called it emits the JS code from the +** built-in file fossil.fossil.js. Subsequent calls are no-ops. +** +** If passed a true value, it emits the contents directly +** to the page output, else it emits a script tag with a +** src=builtin/... to load the script. +** +** Note that this code relies on that loaded via +** style_emit_script_fossil_bootstrap() but it does not call that +** routine. +*/ +void style_emit_script_fetch(int asInline){ + static int once = 0; + if(0==once++){ + style_emit_script_builtin(asInline, "fossil.fetch.js"); + } +} + +/* +** The first time this is called it emits the JS code from the +** built-in file fossil.dom.js. Subsequent calls are no-ops. +** +** If passed a true value, it emits the contents directly +** to the page output, else it emits a script tag with a +** src=builtin/... to load the script. +** +** Note that this code relies on that loaded via +** style_emit_script_fossil_bootstrap(), but it does not call that +** routine. +*/ +void style_emit_script_dom(int asInline){ + static int once = 0; + if(0==once++){ + style_emit_script_builtin(asInline, "fossil.dom.js"); + } +} + +/* +** The first time this is called, it calls style_emit_script_dom(), +** passing it the given asInline value, and emits the JS code from the +** built-in file fossil.tabs.js. Subsequent calls are no-ops. +** +** If passed a true value, it emits the contents directly +** to the page output, else it emits a script tag with a +** src=builtin/... to load the script. +*/ +void style_emit_script_tabs(int asInline){ + static int once = 0; + if(0==once++){ + style_emit_script_dom(asInline); + style_emit_script_builtin(asInline, "fossil.tabs.js"); + } +} + +/* +** The first time this is called it emits the JS code from the +** built-in file fossil.confirmer.js. Subsequent calls are no-ops. +** +** If passed a true value, it emits the contents directly +** to the page output, else it emits a script tag with a +** src=builtin/... to load the script. +*/ +void style_emit_script_confirmer(int asInline){ + static int once = 0; + if(0==once++){ + style_emit_script_builtin(asInline, "fossil.confirmer.js"); + } +} ADDED src/style.fileedit.css Index: src/style.fileedit.css ================================================================== --- /dev/null +++ src/style.fileedit.css @@ -0,0 +1,367 @@ +/** Styles specific to /fileedit... */ +body.fileedit.waiting * { + /* Triggered during AJAX requests. */ + cursor: wait; +} +body.fileedit .error { + padding: 0.25em; +} +body.fileedit .warning { + padding: 0.25em; +} +body.fileedit textarea { + font-family: monospace; + flex: 10 1 auto; + height: initial/*undo damage from some skins*/; +} +body.fileedit textarea:focus, +body.fileedit input:focus{ + /* The sudden appearance of a border (as in the Ardoise skin) + shifts the layout in unsightly ways */ + border: initial; +} +body.fileedit fieldset { + margin: 0.5em 0 0.5em 0; + padding: 0.25em 0; + border-radius: 0.5em; + border-color: inherit; + border-width: 1px; + font-size: 90%; + overflow: auto; +} +body.fileedit fieldset > legend { + margin: 0 0 0 1em; + padding: 0 0.5em 0 0.5em; +} +body.fileedit fieldset > div { + margin: 0 0.25em 0 0.25em; + padding: 0; + overflow: auto; +} +body.fileedit fieldset > div > .input-with-label { + margin: 0.25em 0.5em; +} +body.fileedit fieldset > div > button { + margin: 0.25em 0.5em; +} +body.fileedit .fileedit-hint { + font-size: 80%; + opacity: 0.75; +} +body.fileedit .fileedit-error-report { + background: yellow; + color: darkred; + margin: 1em 0; + padding: 0.5em; + border-radius: 0.5em; +} +body.fileedit code.fileedit-manifest { + display: block; + height: 16em; + overflow: auto; + white-space: pre; +} +body.fileedit div.fileedit-preview { + margin: 0; + padding: 0; +} +body.fileedit #fileedit-tabs { + margin: 1em 0 0 0; +} +body.fileedit #fileedit-tab-preview-wrapper { + overflow: auto; +} +body.fileedit #fileedit-tab-fileselect > h1 { + margin: 0; +} +body.fileedit .fileedit-options.commit-message > div { + display: flex; + flex-direction: column; + align-items: stretch; + font-family: monospace; +} +body.fileedit .fileedit-options.commit-message > div > * { + margin: 0.25em; +} +body.fileedit #fileedit-commit-button-wrapper { + margin: 0.25em; +} +body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options { + margin-top: 0; + border: none; + border-radius: 0; + border-bottom-width: 1px; + border-bottom-style: dotted; +} +body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > button { + vertical-align: middle; + margin: 0.5em; +} +body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > input { + vertical-align: middle; + margin: 0.5em; +} +body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > .input-with-label { + vertical-align: middle; + margin: 0.5em; +} +body.fileedit .fileedit-options > div > * { + margin: 0.25em; +} +body.fileedit .fileedit-options.flex-container.flex-row { + align-items: first baseline; +} +body.fileedit #fileedit-file-selector { + display: flex; + flex-direction: column; + align-content: flex-start; + border-color: inherit; + border-width: 1px; + border-style: inset; + border-radius: 0.5em; + padding: 0 0.25em; + margin: 0; + min-height: 12em; +} +body.fileedit #fileedit-file-selector select { + margin: 0 0 0.5em 0; + height: initial; + font-family: monospace; +} +body.fileedit select:focus { + border: none; +} +body.fileedit option:focus { + border: none; +} +body.fileedit #fileedit-file-selector > div { + padding: 0; + margin: 0; +} +body.fileedit #fileedit-file-selector > div > * { + margin: 0.25em 0.5em 0.25em 0; +} +body.fileedit #fileedit-stash-selector { + margin: 0.25em; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: baseline; +} +body.fileedit #fileedit-stash-selector select { + margin: 0; + height: initial; + font-family: monospace; + flex: 10 1 auto; +} +body.fileedit .tab-container > .tabs > .tab-panel { + display: flex; + flex-direction: column; +} +body.fileedit #fileedit-tab-diff-wrapper { + margin: 0; + padding: 0; + overflow: auto; + display: flex; + flex-direction: column; + align-items: stretch; +} +body.fileedit #fileedit-tab-diff-wrapper > div { + margin: 0.5em 0 0.5em 0; +} +body.fileedit table.sbsdiffcols { + /*width: initial;*/ +} +body.fileedit #fileedit-tab-diff-wrapper > pre.udiff { + margin-top: 0; +} +body.fileedit .sbsdiffcols div.difftxtcol { + display: flex; + flex-direction: column; + align-items: stretch; + width: initial; +} +body.fileedit .sbsdiffcols div.difftxtcol pre { + max-width: 44em; +} + +/** + Styles for fossil.tabs.js. As of this writing, currently + only used by /fileedit, but it is anticipated that these + will eventually need to migrate to default_css.txt for use + in the wiki and/or forum pages when implementing tabbed + ajax-based previews. +*/ +.tab-container { + width: 100%; + display: flex; + flex-direction: column; + align-items: stretch; +} +.tab-container > #fossil-status-bar { + margin-top: 0; +} +.tab-container > .tabs { + padding: 0.25em; + margin: 0; + display: flex; + flex-direction: column; + border-width: 1px; + border-style: outset; + border-color: inherit; +} +.tab-container > .tabs > .tab-panel { + align-self: stretch; + flex: 10 1 auto; + display: block; +} +.tab-container > .tab-bar { + display: flex; + flex-direction: row; + flex: 1 10 auto; + align-self: stretch; + flex-wrap: wrap; +} +.tab-container > .tab-bar > .tab-button { + display: inline-block; + border-radius: 0.5em 0.5em 0 0; + margin: 0 0.1em; + padding: 0.25em 0.75em; + align-self: baseline; + border-color: inherit; + border-width: 1px; + border-bottom: none; + border-top-style: inset; + border-left-style: inset; + border-right-style: inset; + cursor: pointer; + opacity: 0.6; +} +.tab-container > .tab-bar > .tab-button.selected { + text-decoration: underline; + opacity: 1.0; + border-top-style: outset; + border-left-style: outset; + border-right-style: outset; +} + +/** + Styles developed for /fileedit but which have wider + applicability... + + As of this writing, these are only used by /fileedit, but it is + anticipated that they will eventually need to be migrated over to + default_css.txt for use in other pages (specifically wiki and forum + page/post editors). +*/ +.flex-container { + display: flex; +} +.flex-container.flex-row { + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + align-items: center; +} +.flex-container .flex-grow { + flex-grow: 10; + flex-shrink: 0; +} +.flex-container .flex-shrink { + flex-grow: 0; + flex-shrink: 10; +} +.flex-container.flex-row.stretch { + flex-wrap: wrap; + align-items: baseline; + justify-content: stretch; + margin: 0; +} +.flex-container.flex-column { + flex-direction: column; + flex-wrap: wrap; + justify-content: center; + align-items: center; +} +.flex-container.flex-column.stretch { + align-items: stretch; + margin: 0; +} +.flex-container.child-gap-small > * { + margin: 0.25em; +} +#fossil-status-bar { + display: block; + font-family: monospace; + border-width: 1px; + border-style: inset; + border-color: inherit; + min-height: 1.5em; + font-size: 1.2em; + padding: 0.2em; + margin: 0.25em 0; + flex: 0 0 auto; +} +.font-size-100 { + font-size: 100%; +} +.font-size-125 { + font-size: 125%; +} +.font-size-150 { + font-size: 150%; +} +.font-size-175 { + font-size: 175%; +} +.font-size-200 { + font-size: 200%; +} + +/** + .input-with-label is intended to be a wrapper element which + contain both a LABEL tag and an INPUT or SELECT control. + The wrapper is "necessary", as opposed to placing the INPUT + in the LABEL, so that we can include multiple INPUT + elements (e.g. a set of radio buttons). +*/ +.input-with-label { + border: 1px inset #808080; + border-radius: 0.5em; + padding: 0.25em 0.4em; + margin: 0 0.5em; + display: inline-block; + cursor: default; +} +.input-with-label > * { + vertical-align: middle; +} +.input-with-label > label { + display: inline; /* some skins set label display to block! */ +} +.input-with-label > input { + margin: 0; +} +.input-with-label > button { + margin: 0; +} +.input-with-label > select { + margin: 0; +} +.input-with-label > input[type=text] { + margin: 0; +} +.input-with-label > textarea { + margin: 0; +} +.input-with-label > input[type=checkbox] { + vertical-align: sub; +} +.input-with-label > input[type=radio] { + vertical-align: sub; +} +.input-with-label > label { + font-weight: initial; + margin: 0 0.25em 0 0.25em; + vertical-align: middle; +} ADDED src/terminal.c Index: src/terminal.c ================================================================== --- /dev/null +++ src/terminal.c @@ -0,0 +1,130 @@ +/* +** Copyright (c) 2020 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the Simplified BSD License (also +** known as the "2-Clause License" or "FreeBSD License".) + +** This program is distributed in the hope that it will be useful, +** but without any warranty; without even the implied warranty of +** merchantability or fitness for a particular purpose. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +******************************************************************************* +** +** This file contains code used to query terminal info +*/ + +#include "config.h" +#include "terminal.h" +#include +#ifdef _WIN32 +# include +#else +#include +#include +#include +#endif + + + +#if INTERFACE +/* +** Terminal size defined in terms of columns and lines. +*/ +struct TerminalSize { + unsigned int nColumns; /* Number of characters on a single line */ + unsigned int nLines; /* Number of lines */ +}; +#endif + + +/* Get the current terminal size by calling a system service. +** +** Return 1 on success. This sets the size parameters to the values retured by +** the system call, when such is supported; set the size to zero otherwise. +** Return 0 on the system service call failure. +** +** Under Linux/bash the size info is also available from env $LINES, $COLUMNS. +** Or it can be queried using tput `echo -e "lines\ncols"|tput -S`. +** Technically, this info could be cached, but then we'd need to handle +** SIGWINCH signal to requery the terminal on resize event. +*/ +int terminal_get_size(TerminalSize *t){ + memset(t, 0, sizeof(*t)); + +#if defined(TIOCGSIZE) + { + struct ttysize ts; + if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)!=-1 ){ + t->nColumns = ts.ts_cols; + t->nLines = ts.ts_lines; + return 1; + } + return 0; + } +#elif defined(TIOCGWINSZ) + { + struct winsize ws; + if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)!=-1 ){ + t->nColumns = ws.ws_col; + t->nLines = ws.ws_row; + return 1; + } + return 0; + } +#elif defined(_WIN32) + { + CONSOLE_SCREEN_BUFFER_INFO csbi; + if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){ + t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1; + t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; + return 1; + } + return 0; + } +#else + return 1; +#endif +} + +/* +** Return the terminal's current width in columns when available, otherwise +** return the specified default value. +*/ +unsigned int terminal_get_width(unsigned int nDefault){ + TerminalSize ts; + if( terminal_get_size(&ts) ){ + return ts.nColumns; + } + return nDefault; +} + +/* +** Return the terminal's current height in lines when available, otherwise +** return the specified default value. +*/ +unsigned int terminal_get_height(unsigned int nDefault){ + TerminalSize ts; + if( terminal_get_size(&ts) ){ + return ts.nLines; + } + return nDefault; +} + +/* +** COMMAND: test-terminal-size +** +** Show the size of the terminal window from which the command is launched +** as two integers, the width in charaters and the height in lines. +** +** If the size cannot be determined, two zeros are shown. +*/ +void test_terminal_size_cmd(void){ + TerminalSize ts; + terminal_get_size(&ts); + fossil_print("%d %d\n", ts.nColumns, ts.nLines); +} Index: src/th_main.c ================================================================== --- src/th_main.c +++ src/th_main.c @@ -1500,11 +1500,11 @@ if( argc!=3 ){ return Th_WrongNumArgs(interp, "unversioned content FILENAME"); } if( Th_IsRepositoryOpen() ){ Blob content; - if( unversioned_content(argv[2], &content)==0 ){ + if( unversioned_content(argv[2], &content)!=0 ){ Th_SetResult(interp, blob_str(&content), blob_size(&content)); blob_reset(&content); return TH_OK; }else{ return TH_ERROR; Index: src/timeline.c ================================================================== --- src/timeline.c +++ src/timeline.c @@ -1312,11 +1312,11 @@ ){ if( zChng==0 || zChng[0]==0 ) return; blob_append_sql(pSql," AND event.objid IN (" "SELECT mlink.mid FROM mlink, filename" " WHERE mlink.fnid=filename.fnid AND %s)", - glob_expr("filename.name", zChng)); + glob_expr("filename.name", mprintf("\"%s\"", zChng))); } static void addFileGlobDescription( const char *zChng, /* The filename GLOB list */ Blob *pDescription /* Result description */ ){ @@ -1742,10 +1742,11 @@ || (bisectLocal && !g.perm.Setup) ){ login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); return; } + etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA, 0); cookie_read_parameter("y","y"); zType = P("y"); if( zType==0 ){ zType = g.perm.Read ? "ci" : "all"; cgi_set_parameter("y", zType); @@ -1795,10 +1796,11 @@ }else if( fossil_stricmp(zMatchStyle, "regexp")==0 ){ matchStyle = MS_REGEXP; }else{ /* For exact maching, inhibit links to the selected tag. */ zThisTag = zTagName; + Th_Store("current_checkin", zTagName); } /* Display a checkbox to enable/disable display of related check-ins. */ if( advancedMenu ){ style_submenu_checkbox("rel", "Related", 0, 0); @@ -2200,10 +2202,17 @@ "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);" "INSERT OR IGNORE INTO selected_nodes" " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag" " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/ ); + if( zMark ){ + /* If the t=release option is used with m=UUID, then also + ** include the UUID check-in in the display list */ + int ridMark = name_to_rid(zMark); + db_multi_exec( + "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark); + } if( !related ){ blob_append_sql(&cond, " AND blob.rid IN selected_nodes"); }else{ db_multi_exec( "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);" @@ -2426,10 +2435,13 @@ blob_appendf(&desc, " related to tags matching %h", zMatchDesc); }else{ blob_appendf(&desc, " with tags matching %h", zMatchDesc); } } + if( zMark ){ + blob_appendf(&desc," plus check-in \"%h\"", zMark); + } tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; } addFileGlobDescription(zChng, &desc); if( rAfter>0.0 ){ if( rBefore>0.0 ){ Index: src/unversioned.c ================================================================== --- src/unversioned.c +++ src/unversioned.c @@ -85,25 +85,40 @@ } /* ** Initialize pContent to be the content of an unversioned file zName. ** -** Return 0 on success. Return 1 if zName is not found. +** Return 0 on failures. +** Return 1 if the file is found by name. +** Return 2 if the file is found by hash. */ int unversioned_content(const char *zName, Blob *pContent){ Stmt q; - int rc = 1; + int rc = 0; blob_init(pContent, 0, 0); - db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE name=%Q", zName); + db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE name=%Q", + zName); if( db_step(&q)==SQLITE_ROW ){ db_column_blob(&q, 1, pContent); if( db_column_int(&q, 0)==1 ){ blob_uncompress(pContent, pContent); } - rc = 0; + rc = 1; } db_finalize(&q); + if( rc==0 && validate16(zName,-1) ){ + db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE hash=%Q", + zName); + if( db_step(&q)==SQLITE_ROW ){ + db_column_blob(&q, 1, pContent); + if( db_column_int(&q, 0)==1 ){ + blob_uncompress(pContent, pContent); + } + rc = 2; + } + db_finalize(&q); + } return rc; } /* ** Write unversioned content into the database. @@ -328,11 +343,11 @@ int i; verify_all_options(); db_begin_transaction(); for(i=3; i\n\n" unless @ARGV; + +my $out; +if (is_interactive()) { + my $pager = $ENV{PAGER} || which('less') || which('more'); + open $out, '|-', $pager or croak "Cannot pipe to $pager: $!"; +} +else { + $out = *STDOUT; +} + +open my $bcmd, '-|', 'fossil branch current' + or die "Cannot get branch: $!\n"; +my $cbranch = <$bcmd>; +chomp $cbranch; +close $bcmd; + +for my $file (@ARGV) { + my $lastckid; + open my $finfo, '-|', "fossil finfo --brief --limit 0 '$file'" + or die "Failed to get file info: $!\n"; + my @filines = <$finfo>; + close $finfo; + + for my $line (@filines) { + my ($currckid, $date, $user, $branch, @cwords) = split ' ', $line; + next unless $branch eq $cbranch; + if (defined $lastckid and defined $branch) { + my $comment = join ' ', @cwords; + open my $diff, '-|', 'fossil', 'diff', $file, + '--from', $currckid, + '--to', $lastckid, + or die "Failed to diff $currckid -> $lastckid: $!\n"; + my @dl = <$diff>; + close $diff; + my $patch = join '', @dl; + + print $out <<"OUT" +Checkin ID $currckid to $branch by $user on $date +Comment: $comment + +$patch + +OUT + } + + $lastckid = $currckid; + } +} Index: win/Makefile.dmc ================================================================== --- win/Makefile.dmc +++ win/Makefile.dmc @@ -28,13 +28,13 @@ SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen -SRC = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c +SRC = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c -OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O +OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ @@ -49,11 +49,11 @@ $(OBJDIR)\fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res - +echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ + +echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ @@ -836,10 +836,16 @@ $(OBJDIR)\tar$O : tar_.c tar.h $(TCC) -o$@ -c tar_.c tar_.c : $(SRCDIR)\tar.c +translate$E $** > $@ + +$(OBJDIR)\terminal$O : terminal_.c terminal.h + $(TCC) -o$@ -c terminal_.c + +terminal_.c : $(SRCDIR)\terminal.c + +translate$E $** > $@ $(OBJDIR)\th_main$O : th_main_.c th_main.h $(TCC) -o$@ -c th_main_.c th_main_.c : $(SRCDIR)\th_main.c @@ -976,7 +982,7 @@ zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h - +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h + +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers Index: win/Makefile.mingw ================================================================== --- win/Makefile.mingw +++ win/Makefile.mingw @@ -554,10 +554,11 @@ $(SRCDIR)/statrep.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ $(SRCDIR)/tar.c \ + $(SRCDIR)/terminal.c \ $(SRCDIR)/th_main.c \ $(SRCDIR)/timeline.c \ $(SRCDIR)/tkt.c \ $(SRCDIR)/tktsetup.c \ $(SRCDIR)/undo.c \ @@ -640,10 +641,17 @@ $(SRCDIR)/accordion.js \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/copybtn.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ + $(SRCDIR)/fossil.bootstrap.js \ + $(SRCDIR)/fossil.confirmer.js \ + $(SRCDIR)/fossil.dom.js \ + $(SRCDIR)/fossil.fetch.js \ + $(SRCDIR)/fossil.page.fileedit.js \ + $(SRCDIR)/fossil.storage.js \ + $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ @@ -665,10 +673,11 @@ $(SRCDIR)/sounds/b.wav \ $(SRCDIR)/sounds/c.wav \ $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ + $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ @@ -788,10 +797,11 @@ $(OBJDIR)/statrep_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ $(OBJDIR)/tar_.c \ + $(OBJDIR)/terminal_.c \ $(OBJDIR)/th_main_.c \ $(OBJDIR)/timeline_.c \ $(OBJDIR)/tkt_.c \ $(OBJDIR)/tktsetup_.c \ $(OBJDIR)/undo_.c \ @@ -931,10 +941,11 @@ $(OBJDIR)/statrep.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ $(OBJDIR)/tar.o \ + $(OBJDIR)/terminal.o \ $(OBJDIR)/th_main.o \ $(OBJDIR)/timeline.o \ $(OBJDIR)/tkt.o \ $(OBJDIR)/tktsetup.o \ $(OBJDIR)/undo.o \ @@ -1294,10 +1305,11 @@ $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ + $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \ $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ @@ -2269,10 +2281,18 @@ $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c $(OBJDIR)/tar.h: $(OBJDIR)/headers + +$(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE) + $(TRANSLATE) $(SRCDIR)/terminal.c >$@ + +$(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c + +$(OBJDIR)/terminal.h: $(OBJDIR)/headers $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/th_main.c >$@ $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h Index: win/Makefile.mingw.mistachkin ================================================================== --- win/Makefile.mingw.mistachkin +++ win/Makefile.mingw.mistachkin @@ -553,10 +553,11 @@ $(SRCDIR)/statrep.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ $(SRCDIR)/tar.c \ + $(SRCDIR)/terminal.c \ $(SRCDIR)/th_main.c \ $(SRCDIR)/timeline.c \ $(SRCDIR)/tkt.c \ $(SRCDIR)/tktsetup.c \ $(SRCDIR)/undo.c \ @@ -786,10 +787,11 @@ $(OBJDIR)/statrep_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ $(OBJDIR)/tar_.c \ + $(OBJDIR)/terminal_.c \ $(OBJDIR)/th_main_.c \ $(OBJDIR)/timeline_.c \ $(OBJDIR)/tkt_.c \ $(OBJDIR)/tktsetup_.c \ $(OBJDIR)/undo_.c \ @@ -928,10 +930,11 @@ $(OBJDIR)/statrep.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ $(OBJDIR)/tar.o \ + $(OBJDIR)/terminal.o \ $(OBJDIR)/th_main.o \ $(OBJDIR)/timeline.o \ $(OBJDIR)/tkt.o \ $(OBJDIR)/tktsetup.o \ $(OBJDIR)/undo.o \ @@ -1290,10 +1293,11 @@ $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ + $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \ $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ @@ -2257,10 +2261,18 @@ $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c $(OBJDIR)/tar.h: $(OBJDIR)/headers + +$(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE) + $(TRANSLATE) $(SRCDIR)/terminal.c >$@ + +$(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c + +$(OBJDIR)/terminal.h: $(OBJDIR)/headers $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/th_main.c >$@ $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h Index: win/Makefile.msc ================================================================== --- win/Makefile.msc +++ win/Makefile.msc @@ -462,10 +462,11 @@ statrep_.c \ style_.c \ sync_.c \ tag_.c \ tar_.c \ + terminal_.c \ th_main_.c \ timeline_.c \ tkt_.c \ tktsetup_.c \ undo_.c \ @@ -547,10 +548,17 @@ $(SRCDIR)\accordion.js \ $(SRCDIR)\ci_edit.js \ $(SRCDIR)\copybtn.js \ $(SRCDIR)\diff.tcl \ $(SRCDIR)\forum.js \ + $(SRCDIR)\fossil.bootstrap.js \ + $(SRCDIR)\fossil.confirmer.js \ + $(SRCDIR)\fossil.dom.js \ + $(SRCDIR)\fossil.fetch.js \ + $(SRCDIR)\fossil.page.fileedit.js \ + $(SRCDIR)\fossil.storage.js \ + $(SRCDIR)\fossil.tabs.js \ $(SRCDIR)\graph.js \ $(SRCDIR)\href.js \ $(SRCDIR)\login.js \ $(SRCDIR)\markdown.md \ $(SRCDIR)\menu.js \ @@ -572,10 +580,11 @@ $(SRCDIR)\sounds\b.wav \ $(SRCDIR)\sounds\c.wav \ $(SRCDIR)\sounds\d.wav \ $(SRCDIR)\sounds\e.wav \ $(SRCDIR)\sounds\f.wav \ + $(SRCDIR)\style.fileedit.css \ $(SRCDIR)\tree.js \ $(SRCDIR)\useredit.js \ $(SRCDIR)\wiki.wiki OBJ = $(OX)\add$O \ @@ -697,10 +706,11 @@ $(OX)\statrep$O \ $(OX)\style$O \ $(OX)\sync$O \ $(OX)\tag$O \ $(OX)\tar$O \ + $(OX)\terminal$O \ $(OX)\th$O \ $(OX)\th_lang$O \ $(OX)\th_main$O \ $(OX)\th_tcl$O \ $(OX)\timeline$O \ @@ -902,10 +912,11 @@ echo $(OX)\statrep.obj >> $@ echo $(OX)\style.obj >> $@ echo $(OX)\sync.obj >> $@ echo $(OX)\tag.obj >> $@ echo $(OX)\tar.obj >> $@ + echo $(OX)\terminal.obj >> $@ echo $(OX)\th.obj >> $@ echo $(OX)\th_lang.obj >> $@ echo $(OX)\th_main.obj >> $@ echo $(OX)\th_tcl.obj >> $@ echo $(OX)\timeline.obj >> $@ @@ -1753,10 +1764,16 @@ $(OX)\tar$O : tar_.c tar.h $(TCC) /Fo$@ -c tar_.c tar_.c : $(SRCDIR)\tar.c translate$E $** > $@ + +$(OX)\terminal$O : terminal_.c terminal.h + $(TCC) /Fo$@ -c terminal_.c + +terminal_.c : $(SRCDIR)\terminal.c + translate$E $** > $@ $(OX)\th_main$O : th_main_.c th_main.h $(TCC) /Fo$@ -c th_main_.c th_main_.c : $(SRCDIR)\th_main.c @@ -2014,10 +2031,11 @@ statrep_.c:statrep.h \ style_.c:style.h \ sync_.c:sync.h \ tag_.c:tag.h \ tar_.c:tar.h \ + terminal_.c:terminal.h \ th_main_.c:th_main.h \ timeline_.c:timeline.h \ tkt_.c:tkt.h \ tktsetup_.c:tktsetup.h \ undo_.c:undo.h \ Index: www/cgi.wiki ================================================================== --- www/cgi.wiki +++ www/cgi.wiki @@ -14,12 +14,12 @@ script]. CGI is the technique that the three [./selfhost.wiki|self-hosting Fossil repositories] all use. Setting up a Fossil server using CGI is mostly about writing a short script (usually just 2 lines line) in the cgi-bin folder of an ordinary -web-browser. But there are a lot of extra options that can be added -to this script, to customize the configuration. This article descripts +web-server. But there are a lot of extra options that can be added +to this script, to customize the configuration. This article describes those options.

CGI Script Options

The CGI script used to launch a Fossil server will usually look something Index: www/changes.wiki ================================================================== --- www/changes.wiki +++ www/changes.wiki @@ -1,9 +1,14 @@ Change Log + +

Changes for Version 2.12 (pending)

+ + * (no changes yet...) + -

Changes for Version 2.11 (pending)

+

Changes for Version 2.11 (2020-05-25)

* Support [/md_rules|Markdown] in the default ticket configuration. * Timestamp strings in [./checkin_names.wiki|object names] can now omit punctation. So, for example, "202004181942" and "2020-04-18 19:42" mean the same thing. @@ -55,10 +60,12 @@ * Security: Fossil now puts the Content-Security-Policy in the HTTP reply header, in addition to also leaving it in the HTML <head> section, so that it is always available, even if a custom skin overrides the HTML <head> and omits the CSP in the process. + * Output of the [/help?cmd=diff|fossil diff -y] command automatically + adjusts according to the terminal width. * The Content-Security-Policy is now set using the [/help?cmd=default-csp|default-csp setting]. * Merge conflicts caused via the [/help?cmd=merge|merge] and [/help?cmd=update|update] commands no longer leave temporary files behind unless the new --keep-merge-files flag ADDED www/fileedit-page.md Index: www/fileedit-page.md ================================================================== --- /dev/null +++ www/fileedit-page.md @@ -0,0 +1,260 @@ +# The fileedit Page + +This document describes the limitations of, caveats for, and +disclaimers for the [](/fileedit) page, which provides users with +[checkin privileges](./caps/index.md) basic editing features for files +via the web interface. + +# Important Caveats and Disclaimers + +Predictably, the ability to edit files in a repository from a web +browser halfway around the world comes with several obligatory caveats +and disclaimers... + +## `/fileedit` Does *Nothing* by Default. + +In order to "activate" it, a user with [the "setup" +permission](./caps/index.md) must set the +[fileedit-glob](/help?cmd=fileedit-glob) repository setting to a +comma- or newline-delimited list of globs representing a whitelist of +files which may be edited online. Any user with commit access may then +edit files matching one of those globs. Certain pages within the UI +get an "edit" link added to them when the current user's permissions +and the whitelist both permit editing of that file. + +## CSRF & HTTP Referrer Headers + +In order to protect against [Cross-site Request Forgery (CSRF)][csrf] +attacks, Fossil UI features which write to the database require that +the browser send the so-called [HTTP `Referer` header][referer] +(noting that the misspelling of "referrer" is a historical accident +which has long-since been standardized!). Modern browsers, by default, +include such information automatically for *interactive* actions which +lead to a request, e.g. clicking on a link back to the same +server. However, `/fileedit` uses asynchronous ["XHR"][xhr] +connections, which browsers *may* treat differently than strictly +interactive elements. + +- **Firefox**: configuration option `network.http.sendRefererHeader` + specifies whether the `Referer` header is sent. It must have a value + of 2 (which is the default) for XHR requests to get the `Referer` + header. Purely interactive Fossil features, in which users directly + activate links or forms, work with a level of 1 or higher. +- **Chrome**: apparently requires an add-on in order to change this + policy, so Chrome without such an add-on will not suppress this + header. +- **Safari**: ??? +- **Other browsers**: ??? + +If `/filepage` shows an error message saying "CSRF violation," the +problem is that the browser is not sending a `Referer` header to XHR +connections. Fossil does not offer a way to disable its CSRF +protections. + +[referer]: https://en.wikipedia.org/wiki/HTTP_referer +[csrf]: https://en.wikipedia.org/wiki/Cross-site_request_forgery +[xhr]: https://en.wikipedia.org/wiki/XMLHttpRequest + +## `/fileedit` **Works by Creating Commits** + +Thus any edits made via that page become a normal part of the +repository's blockchain. + +## `/fileedit` is *Intended* for use with Embedded Docs + +... and similar text files, and is most certainly +**not intended for editing code**. + +Editing files with unusual syntax requirements, e.g. hard tabs in +makefiles, may break them. *You Have Been Warned.* + +Similarly, though every effort is made to retain the end-of-line +style used by being-edited files, the round-trip through an HTML +textarea element may change the EOLs. The Commit section of the page +offers three different options for how to treat newlines when saving +changes. **Files with mixed EOL styles** *will be normalized to a single +EOL style* when modified using `/fileedit`. When "inheriting" the EOL +style from a previous version which has mixed styles, the first EOL +style detected in the previous version of the file is used. + +## `/fileedit` **is Not a Replacement for a Checkout** + +A full-featured checkout allows far more possibilities than this basic +online editor permits, and the feature scope of `/fileedit` is +intentionally kept small, implementing only the bare necessities +needed for performing basic edits online. It *is not, and will never +be, a replacement for a checkout.* + +It is to be expected that users will want to do "more" with this +page, and we generally encourage feature requests, but be aware that +certain types of ostensibly sensible feature requests *will be +rejected* for `/fileedit`. These include, but are not limited to: + +- Features which are already provided by other pages, e.g. +the ability to create a new named branch or add tags. +- Features which would require re-implementing significant +capabilities provided only within a checkout (e.g. merging files). +- The ability to edit/manipulate files which are in a local +checkout. (If you have a checkout, use your local editor, not +`/fileedit`.) +- Editing of non-text files, e.g. images. Use a checkout and your +preferred graphics editor. +- Support for syncing/pulling/pushing of a repository before and/or +after edits. Those features cannot be *reliably* provided via a web +interface for several reasons. + +Similarly, some *potential* features have significant downsides, +abuses, and/or implementation hurdles which make the decision of +whether or not to implement them subject to notable contributor +debate. e.g. the ability to add new files or remove/rename older +files. + + +## `/fileedit` **Stores Only Limited Local Edits While Working** + +When changes are made to a given checkin/file combination, +`/fileedit` will, if possible, store them in [`window.localStorage` +or `window.sessionStorage`][html5storage], if available, but... + +- Which storage is used is unspecified and may differ across + environments. +- If neither of those is available, the storage is transient and + will not survive a page reload. In this case, the UI issues a clear + warning in the editor tab. +- It stores only the most recent checkin/file combinations which have + been modified (exactly how many may differ - the number will be + noted somewhere in the UI). Note that changing the "executable bit" + is counted as a modification, but the checkin *comment* is *not* + and is reset after a commit. +- If its internal limit on the number of modified files is exceeded, + it silently discards the oldest edits to keep the list at its limit. + +Edits are saved whenever the editor component fires its "change" +event, which essentially means as soon as it loses input focus. Thus +to force the browser to save any pending changes, simply click +somwhere on the page outside of the editor. + +Exactly how long `localStorage` will survive, and how much it or +`sessionStorage` can hold, is environment-dependent. `sessionStorage` +will survive until the current browser tab is closed, but it survives +across reloads of the same tab. + +If `/filepage` determines that no peristent storage is available a +warning is displayed on the editor page. + +[html5storage]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API + +## The Power is Yours, but... + +> "With great power comes great responsibility." + +**Use this feature judiciously, *if at all*.** + +Now, with those warnings and caveats out of the way... + +----- + +# Tips and Tricks + +## `fossil` Global-scope JS Object + +`/fileedit` is largely implemented in JavaScript, and makes heavy use +of the global-scope `fossil` object, which provides +infrastructure-level features intended for use by Fossil UI pages. +(That said, that infrastructure was introduced with `/fileedit`, and +most pages do not use it.) + +The `fossil.page` object represents the UI's current page (on pages +which make use of this API - most do not). That object supports +listening to page-specific events so that JS code installed via +[client-side edits to the site skin's footer](customskin.md) may react +to those changes somehow. The next section describes one such use for +such events... + +## Integrating Syntax Highlighting + +Assuming a repository has integrated a 3rd-party syntax highlighting +solution, it can probably (depending on its API) be told how to +highlight `/fileedit`'s wiki/markdown-format previews. Here are +instructions for doing so with [highlightjs](https://highlightjs.org/): + +At the very bottom of the [site skin's footer](customskin.md), add a +script tag similar to the following: + +```javascript + +``` + +Note that the `nonce="$"` part is intended to be entered +literally as shown above. It will be expanded to contain the current +request's nonce value when the page is rendered. + +The first line of the script just ensures that the expected JS-level +infrastructure is loaded. It's only loaded in the `/fileedit` page and +possibly pages added or "upgraded" since `/fileedit`'s introduction. + +The part in the `if` block adds an event listener to the `/filepage` +app which gets called when the preview is refreshed. That event +contains 3 properties: + +- `previewMode`: a string describing the current preview mode: `wiki` + (which includes Fossil-native wiki and markdown), `text`, + `htmlInline`, `htmlIframe`. We should "probably" only highlight wiki + text, and thus the example above limits its work to that type of + preview. It won't work with `htmlIframe`, as that represents an + iframe element which contains a complete HTML document. +- `element`: the DOM element in which the preview is rendered. +- `mimetype`: the mimetype of the being-previewed content, as determined + by Fossil (by its file extension). + +The event listener callback shown above doesn't use the `mimetype`, +but makes used of the other two. It fishes all `code` blocks out of +the preview which explicitly have a CSS class named +`language-`something, and then asks highlightjs to highlight them. + +## Integrating a Custom Editor Widget + +*Hypothetically*, though this is currently unproven "in the wild," it +is possible to replace `/filepage`'s basic text-editing widget (a +`textarea` element) with a fancy 3rd-party editor widget by doing the +following: + +First, install proxy functions so that `fossil.page.fileContent()` +can get and set your content: + +``` +fossil.page.setFileContentMethods( + function(){ return text-form content of your widget }, + function(content){ set text-form content of your widget } +}; +``` + +Secondly, inject the custom editor widget into the UI, replacing +the default editor widget: + +```javascript +fossil.page.replaceEditorWidget(yourNewWidgetElement); +``` + +That method must be passed a DOM element and may only be called once: +it *removes itself* the first time it is called. + +That "should" be all there is to it. When `fossil.page` needs to get +the being-edited content, it will call `fossil.page.fileContent()` +with no arguments, and when it sets the content (immediately after +(re)loading a file), it will pass that content to +`fossil.page.fileContent()`. Those, in turn will trigger the installed +proxies and fire any related events. Index: www/index.wiki ================================================================== --- www/index.wiki +++ www/index.wiki @@ -9,11 +9,11 @@
  • [./build.wiki | Install]
  • [https://fossil-scm.org/forum | Support/Forum ]
  • [./hints.wiki | Tips & Hints]
  • [./changes.wiki | Change Log]
  • [../COPYRIGHT-BSD2.txt | License] -
  • [./userlinks.wiki | User inks] +
  • [./userlinks.wiki | User Links]
  • [./hacker-howto.wiki | Hacker How-To]
  • [./fossil-v-git.wiki | Fossil vs. Git]
  • [./permutedindex.html | Documentation Index] Index: www/mkindex.tcl ================================================================== --- www/mkindex.tcl +++ www/mkindex.tcl @@ -41,10 +41,11 @@ embeddeddoc.wiki {Embedded Project Documentation} encryptedrepos.wiki {How To Use Encrypted Repositories} env-opts.md {Environment Variables and Global Options} event.wiki {Events} faq.wiki {Frequently Asked Questions} + fileedit-page.md {The fileedit Page} fileformat.wiki {Fossil File Format} fiveminutes.wiki {Up and Running in 5 Minutes as a Single User} forum.wiki {Fossil Forums} foss-cklist.wiki {Checklist For Successful Open-Source Projects} fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE} Index: www/permutedindex.html ================================================================== --- www/permutedindex.html +++ www/permutedindex.html @@ -11,11 +11,11 @@
  • Quick-start Guide
  • Purpose and History of Fossil
  • Compiling and installing Fossil
  • License
  • List of commands, web-pages, and settings -
  • Key Docs for Fossil Users +
  • Miscellaneous Docs for Fossil Users
  • Fossil Developer's Guide
  • Jim Schimpf's book @@ -113,10 +113,11 @@
  • Extensions — CGI Server
  • Extensions To A Fossil Server Using CGI Scripts — Adding
  • Features To Fossil — Adding New
  • File Format — Fossil
  • File Name Glob Patterns
  • +
  • fileedit Page — The
  • Files — Unversioned
  • Forking, Merging, and Tagging — Branching,
  • Format — Fossil Delta
  • Format — Fossil File
  • Format vs Fossil Repo Size — Image
  • @@ -206,10 +207,11 @@
  • Operation — Principles Of
  • Options — CGI Script Configuration
  • Options — Environment Variables and Global
  • Overview Of The Design And Implementation Of Fossil — A Technical
  • Page — Home
  • +
  • Page — The fileedit
  • Page Works — How The Download
  • Pages — Theming: Customizing The Appearance of Web
  • Password Management And Authentication
  • Patterns — File Name Glob
  • People Are Saying About Fossil, Git, and DVCSes in General — Quotes: What
  • @@ -281,10 +283,11 @@
  • Testing Checklist — Pre-Release
  • TH1 Scripting Language — The
  • The "Backoffice" mechanism of Fossil
  • The Annotate/Blame Algorithm Of Fossil
  • The Default Content Security Policy
  • +
  • The fileedit Page
  • The Fossil Build Process
  • The Fossil Sync Protocol
  • The Fossil Ticket System
  • The Fossil Web Interface
  • The Purpose And History Of Fossil