diff --git a/collects/drscheme/private/drsig.ss b/collects/drscheme/private/drsig.ss index 3f50de4751..5cad50c0ea 100644 --- a/collects/drscheme/private/drsig.ss +++ b/collects/drscheme/private/drsig.ss @@ -200,9 +200,7 @@ goto-release-notes goto-plt-license help-desk - get-docs - open-url - add-help-desk-font-prefs)) + get-docs)) (define-signature drscheme:language^ (get-default-mixin diff --git a/collects/drscheme/private/help-desk.ss b/collects/drscheme/private/help-desk.ss index ace512e9ac..49ac63e632 100644 --- a/collects/drscheme/private/help-desk.ss +++ b/collects/drscheme/private/help-desk.ss @@ -3,7 +3,8 @@ (require (lib "string-constant.ss" "string-constants") (lib "mred.ss" "mred") (lib "external.ss" "browser") - (lib "help-desk.ss" "help") + (lib "bug-report.ss" "help") + (lib "buginfo.ss" "help" "private") (lib "framework.ss" "framework") (lib "class.ss") (lib "list.ss") @@ -13,10 +14,9 @@ (import [prefix drscheme:frame: drscheme:frame^] [prefix drscheme:language-configuration: drscheme:language-configuration/internal^]) - (export (rename drscheme:help-desk^ - [-add-help-desk-font-prefs add-help-desk-font-prefs])) + (export drscheme:help-desk^) - (define (-add-help-desk-font-prefs b) (add-help-desk-font-prefs b)) + (define (-add-help-desk-font-prefs b) '(add-help-desk-font-prefs b)) ;; : -> string (define (get-computer-language-info) @@ -32,88 +32,8 @@ (send language get-language-position) (send language marshall-settings settings))))) - ;; get-docs : (listof (cons path[short-dir-name] string[doc full name])) - (define (get-docs) - (let ([dirs (find-doc-names)]) - (map (λ (pr) - (let-values ([(base name dir?) (split-path (car pr))]) - (cons name (cdr pr)))) - dirs))) - (set-bug-report-info! "Computer Language" get-computer-language-info) - (define drscheme-help-desk-mixin - (mixin (help-desk-frame<%> frame:standard-menus<%>) () - (define/override (file-menu:create-open-recent?) #t) - - (define/override (file-menu:new-callback x y) - (handler:edit-file #f) - #t) - (define/override (file-menu:between-save-as-and-print menu) - (new separator-menu-item% (parent menu))) - - (define current-language - (preferences:get drscheme:language-configuration:settings-preferences-symbol)) - (define/public (set-current-language cl) - (set! current-language cl)) - - (define/override (order-manuals x) - (send (drscheme:language-configuration:language-settings-language current-language) - order-manuals - x)) - (define/override (get-language-name) - (send (drscheme:language-configuration:language-settings-language current-language) - get-language-name)) - - (define/override (file-menu:between-new-and-open file-menu) - (instantiate menu:can-restore-menu-item% () - (label (string-constant plt:hd:new-help-desk)) - (parent file-menu) - (callback (λ (x y) (new-help-desk)))) - (super file-menu:between-new-and-open file-menu)) - - (super-new) - - (inherit get-menu-bar) - (inherit-field choices-panel) - (letrec ([language-menu (new menu% - (parent (get-menu-bar)) - (label (string-constant language-menu-name)))] - [change-language-callback - (λ () - (let ([new-settings (drscheme:language-configuration:language-dialog - #f - current-language - this - #t)]) - (when new-settings - (set! current-language new-settings) - (send lang-message set-msg (get-language-name)) - (preferences:set - drscheme:language-configuration:settings-preferences-symbol - new-settings))))] - [lang-message - (new lang-message% - (button-release (λ () (change-language-callback))) - (parent choices-panel) - (font normal-control-font))] - [language-item (new menu-item% - (label (string-constant choose-language-menu-item-label)) - (parent language-menu) - (shortcut #\l) - (callback - (λ (x y) - (change-language-callback))))]) - (frame:reorder-menus this) - (send lang-message set-msg (get-language-name)) - - ;; move the grow box spacer pane to the end - (send choices-panel change-children - (λ (l) - (append - (filter (λ (x) (not (is-a? x grow-box-spacer-pane%))) l) - (list (car (filter (λ (x) (is-a? x grow-box-spacer-pane%)) l))))))))) - (define lang-message% (class canvas% (init-field button-release font) @@ -145,40 +65,22 @@ (send dc draw-text dots (- cw dw) (- (/ ch 2) (/ th 2)))])))) (super-new))) + (define (goto-manual-link a b) (error 'goto-maual-link "~s ~s" a b)) + (define (goto-hd-location b) (error 'goto-hd-location "~s" b)) + (define (goto-help manual link) (goto-manual-link manual link)) (define (goto-tour) (goto-hd-location 'hd-tour)) (define (goto-release-notes) (goto-hd-location 'release-notes)) (define (goto-plt-license) (goto-hd-location 'plt-license)) + (define (get-docs) (error 'help-desk.ss "get-docs")) + (define help-desk (case-lambda - [() (show-help-desk)] + [() (void)] [(key) (help-desk key #f)] [(key lucky?) (help-desk key lucky? 'keyword+index)] [(key lucky? type) (help-desk key lucky? type 'contains)] [(key lucky? type mode) (help-desk key lucky? type mode #f)] [(key lucky? type mode language) - (let ([frame (or (find-help-desk-frame) - (new-help-desk))]) - (when language - (send frame set-current-language language)) - (search-for-docs/in-frame - frame - key - (case type - [(keyword) "keyword"] - [(keyword+index) "keyword-index"] - [(keyword+index+text) "keyword-index-text"] - [else (error 'drscheme:help-desk:help-desk "unknown type argument: ~s" type)]) - (case mode - [(exact) "exact-match"] - [(contains) "containing-match"] - [(regexp) "regexp-match"] - [else (error 'drscheme:help-desk:help-desk "unknown mode argument: ~s" mode)]) - lucky? - (map car (get-docs))))])) - - ;; open-url : string -> void - (define (open-url x) (send-url x)) - - (add-help-desk-mixin drscheme-help-desk-mixin) + (void)])) diff --git a/collects/drscheme/private/main.ss b/collects/drscheme/private/main.ss index 0234d210a4..577e035d01 100644 --- a/collects/drscheme/private/main.ss +++ b/collects/drscheme/private/main.ss @@ -204,7 +204,6 @@ list?) (drscheme:font:setup-preferences) - (drscheme:help-desk:add-help-desk-font-prefs #t) (color-prefs:add-background-preferences-panel) (scheme:add-preferences-panel) (scheme:add-coloring-preferences-panel) diff --git a/collects/drscheme/private/tool-contracts.ss b/collects/drscheme/private/tool-contracts.ss index 202b5d2f72..4a85ea3c3c 100644 --- a/collects/drscheme/private/tool-contracts.ss +++ b/collects/drscheme/private/tool-contracts.ss @@ -1325,12 +1325,6 @@ ; ; ; ; - (drscheme:help-desk:open-url - (string? . -> . void?) - (url) - - "Opens \\var{url} in a new help desk window.") - (drscheme:help-desk:help-desk (case-> (-> void?) diff --git a/collects/help/bug-report.ss b/collects/help/bug-report.ss new file mode 100644 index 0000000000..984597fd62 --- /dev/null +++ b/collects/help/bug-report.ss @@ -0,0 +1,542 @@ + +(module bug-report mzscheme + (require (lib "string-constant.ss" "string-constants") + (lib "head.ss" "net") + (lib "mred.ss" "mred") + (lib "framework.ss" "framework") + (lib "class.ss") + (lib "etc.ss") + (lib "list.ss") + (lib "url.ss" "net") + (lib "uri-codec.ss" "net") + (lib "htmltext.ss" "browser") + (lib "dirs.ss" "setup") + "private/buginfo.ss" + "private/manuals.ss") + + (provide help-desk:report-bug) + + (define bug-www-server "bugs.plt-scheme.org") + (define bug-www-server-port 80) + + ;; this one should be defined by help desk. + (define frame-mixin + (namespace-variable-value 'help-desk:frame-mixin #f (lambda () (lambda (x) x)))) + + (preferences:set-default 'drscheme:email "" string?) + (preferences:set-default 'drscheme:full-name "" string?) + + (define bug-frame% + (class (frame-mixin (frame:standard-menus-mixin frame:basic%)) + (init title) + + ;; a bunch of stuff we don't want + (define/override (file-menu:between-print-and-close menu) (void)) + (define/override (edit-menu:between-find-and-preferences menu) (void)) + (define/override (file-menu:create-open?) #f) + (define/override (file-menu:create-open-recent?) #f) + (define/override (file-menu:create-new?) #f) + (define/override (file-menu:create-save?) #f) + (define/override (file-menu:create-revert?) #f) + + (field (ok-to-close? #f)) + (public set-ok-to-close) + (define (set-ok-to-close ok?) (set! ok-to-close? #t)) + (define/augment (can-close?) + (or ok-to-close? + (ask-yes-or-no (string-constant cancel-bug-report?) + (string-constant are-you-sure-cancel-bug-report?) + this))) + + (super-make-object title))) + + + (define (help-desk:report-bug) + (define bug-frame (instantiate bug-frame% () (title (string-constant bug-report-form)))) + (define single (new panel:single% (parent (send bug-frame get-area-container)))) + (define outermost-panel (make-object vertical-panel% single)) + + (define response-panel (new vertical-panel% (parent single))) + (define response-text (new (html-text-mixin text%) (auto-wrap #t))) + (define response-ec (new editor-canvas% (parent response-panel) (editor response-text))) + (define response-button-panel (new horizontal-panel% + (stretchable-height #f) + (parent response-panel) + (alignment '(right center)))) + (define cancel-kill-thread #f) + (define response-reset (new button% + (parent response-button-panel) + (enabled #f) + (label (string-constant dialog-back)) + (callback + (lambda (x y) + (switch-to-compose-view))))) + (define response-abort (new button% + (parent response-button-panel) + (enabled #f) + (callback + (lambda (x y) + (kill-thread cancel-kill-thread) + (switch-to-compose-view))) + (label (string-constant abort)))) + (define response-close (new button% + (parent response-button-panel) + (enabled #f) + (callback (lambda (x y) (cleanup-frame))) + (label (string-constant close)))) + (define stupid-internal-define-syntax1 + (new grow-box-spacer-pane% (parent response-button-panel))) + + (define top-panel (make-object vertical-panel% outermost-panel)) + + (define (switch-to-response-view) + (send response-text lock #f) + (send response-text erase) + (render-html-to-text ; hack to get nice text in + (open-input-string + " 




Submitting bug report...

") + response-text #t #f) + (send response-text lock #t) + (send single active-child response-panel)) + (define (switch-to-compose-view) + (send single active-child outermost-panel) + (send (if (string=? "" (preferences:get 'drscheme:full-name)) + name + summary) + focus)) + + (define lps null) + + ; build/label : ((union string (list-of string)) + ; (area-container<%> -> item<%>) + ; boolean + ; area-container<%> + ; -> item<%>) + ; constructs and arranges the gui objects for the bug report form + ; effect: updates lps with the new label panel, for future alignment + (define build/label + (opt-lambda (text make-item top? [stretch? #f] [top-panel top-panel] [vertical? #f]) + (let*-values ([(hp) (make-object (if vertical? + vertical-panel% + horizontal-panel%) + top-panel)] + [(lp) (make-object vertical-panel% hp)] + [(ip) (make-object vertical-panel% hp)] + [(label/s) (if (string? text) + (make-object message% text lp) + (map (lambda (s) + (make-object message% s lp)) + text))] + [(item) (make-item ip)]) + (set! lps (cons lp lps)) + (unless stretch? + (send hp stretchable-height #f) + (send lp stretchable-height #f) + (send ip stretchable-height #f)) + (send lp stretchable-width #f) + (send lp stretchable-height #f) + (send lp set-alignment (if vertical? 'left 'right) (if top? 'top 'center)) + (send ip set-alignment 'left 'top) + item))) + + (define (align-labels) + (let ([width (apply max (map (lambda (x) (send (car (send x get-children)) min-width)) + lps))]) + (for-each (lambda (x) (send x min-width width)) lps))) + + (define name + (build/label + (string-constant bug-report-field-name) + (lambda (panel) + (keymap:call/text-keymap-initializer + (lambda () + (make-object text-field% #f panel + (lambda (text event) + (preferences:set 'drscheme:full-name (send text get-value))) + (preferences:get 'drscheme:full-name))))) + #f)) + + (define email + (build/label + (string-constant bug-report-field-email) + (lambda (panel) + (keymap:call/text-keymap-initializer + (lambda () + (make-object text-field% #f panel + (lambda (text event) + (preferences:set 'drscheme:email (send text get-value))) + (preferences:get 'drscheme:email))))) + #f)) + + (define summary + (build/label + (string-constant bug-report-field-summary) + (lambda (panel) + (keymap:call/text-keymap-initializer + (lambda () + (make-object text-field% #f panel void)))) + #f)) + + (define severity + (build/label + (string-constant bug-report-field-severity) + (lambda (panel) + (make-object choice% + #f + (list "critical" "serious" "non-critical") + panel + void)) + #f)) + + (define bug-classes '(("software bug" "sw-bug") + ("documentation bug" "doc-bug") + ("change request" "change-request") + ("support" "support"))) + + (define bug-class + (build/label + (string-constant bug-report-field-class) + (lambda (panel) + (make-object choice% + #f + (map car bug-classes) + panel + void)) + #f)) + + (define (translate-class class) + (cadr (assoc class bug-classes))) + + (define (make-big-text label . args) + (let ([canvas + (apply + build/label + label + (lambda (panel) + (let* ([text (new (editor:standard-style-list-mixin + (editor:keymap-mixin + text:basic%)))] + [canvas (new canvas:basic% + (style '(hide-hscroll)) + (parent panel) + (editor text))]) + (send text set-paste-text-only #t) + (send text auto-wrap #t) + (send text set-styles-fixed #t) + canvas)) + #t + args)]) + (send canvas min-width 500) + (send canvas min-height 130) + (send canvas get-editor) + (send canvas allow-tab-exit #t) + canvas)) + + (define description (make-big-text (string-constant bug-report-field-description) #t)) + (define reproduce (make-big-text (list (string-constant bug-report-field-reproduce1) + (string-constant bug-report-field-reproduce2)) + #t)) + + (define synthesized-dialog (make-object dialog% (string-constant bug-report-synthesized-information))) + (define synthesized-panel (make-object vertical-panel% synthesized-dialog)) + (define synthesized-button-panel + (new horizontal-panel% [parent synthesized-dialog] + [alignment '(right center)] [stretchable-height #f])) + (define synthesized-ok-button (make-object button% (string-constant ok) synthesized-button-panel + (lambda (x y) + (send synthesized-dialog show #f)))) + (define synthesized-info-shown? #t) + (define (show-synthesized-info) + (send synthesized-dialog show #t)) + + (define version + (build/label + (string-constant bug-report-field-version) + (lambda (panel) + (keymap:call/text-keymap-initializer + (lambda () + (make-object text-field% #f panel void "")))) + #f + #f + synthesized-panel + #f)) + (define environment + (build/label + (string-constant bug-report-field-environment) + (lambda (panel) + (keymap:call/text-keymap-initializer + (lambda () + (make-object text-field% #f panel void "")))) + #f + #f + synthesized-panel + #f)) + + (define human-language + (build/label + (string-constant bug-report-field-human-language) + (lambda (panel) + (keymap:call/text-keymap-initializer + (lambda () + (make-object text-field% #f panel void "")))) + #f + #f + synthesized-panel)) + + (define memory-use + (build/label + (string-constant bug-report-field-memory-use) + (lambda (panel) + (keymap:call/text-keymap-initializer + (lambda () + (make-object text-field% #f panel void "")))) + #f + #f + synthesized-panel)) + + (define docs-installed + (make-big-text + (string-constant bug-report-field-docs-installed) + #t + synthesized-panel)) + + (define collections + (make-big-text + (string-constant bug-report-field-collections) + #t + synthesized-panel)) + + (define extras + (map (lambda (bri) + (let ([label (bri-label bri)]) + (cons + label + (build/label + label + (lambda (panel) + (let ([field + (keymap:call/text-keymap-initializer + (lambda () + (make-object text-field% #f panel void "")))]) + (send field set-value (bri-value bri)) + field)) + #f + #f + synthesized-panel)))) + (get-bug-report-infos))) + + (define button-panel + (new horizontal-panel% [parent outermost-panel] + [alignment '(right center)] [stretchable-height #f])) + (define synthesized-button (make-object button% + (string-constant bug-report-show-synthesized-info) + button-panel (lambda x (show-synthesized-info)))) + (define _spacer (new horizontal-pane% (parent button-panel))) + (define cancel-button (make-object button% (string-constant cancel) button-panel (lambda x (cancel)))) + (define ok-button (make-object button% (string-constant bug-report-submit) button-panel (lambda x (ok)))) + (define _grow-box + (new grow-box-spacer-pane% [parent button-panel])) + + (define (get-query) + (list (cons 'help-desk "true") + (cons 'replyto (preferences:get 'drscheme:email)) + (cons 'originator (preferences:get 'drscheme:full-name)) + (cons 'subject (send summary get-value)) + (cons 'severity (send severity get-string-selection)) + (cons 'class (translate-class (send bug-class get-string-selection))) + (cons 'release (send version get-value)) + (cons 'description (apply string-append (map (lambda (x) (string-append x "\n")) + (get-strings description)))) + (cons 'how-to-repeat (apply string-append + (map (lambda (x) (string-append x "\n")) + (get-strings reproduce)))) + (cons 'platform (get-environment)))) + + (define (get-environment) + (string-append (send environment get-value) + "\n" + "Docs Installed:\n" + (format "~a" (send (send docs-installed get-editor) get-text)) + "\n" + (format "Human Language: ~a\n" (send human-language get-value)) + (format "(current-memory-use) ~a\n" (send memory-use get-value)) + "\nCollections:\n" + (format "~a" (send (send collections get-editor) get-text)) + "\n" + (apply + string-append + (map (lambda (extra) + (format "~a: ~a\n" + (car extra) + (send (cdr extra) get-value))) + extras)))) + + ; send-bug-report : (-> void) + ;; initiates sending the bug report and switches the GUI's mode + (define (send-bug-report) + (letrec ([query (get-query)] + [url + (string->url (format "http://~a:~a/cgi-bin/bug-report" + bug-www-server + bug-www-server-port))] + [post-data + (parameterize ([current-alist-separator-mode 'amp]) + (string->bytes/utf-8 (alist->form-urlencoded query)))] + [http-thread + (parameterize ([current-custodian (make-custodian)]) + (thread + (lambda () + (with-handlers ([(lambda (x) (exn:break? x)) + (lambda (x) (void))] + [(lambda (x) (not (exn:break? x))) + (lambda (x) + (queue-callback + (lambda () + (switch-to-compose-view) + (message-box + (string-constant error-sending-bug-report) + (format (string-constant error-sending-bug-report-expln) + (if (exn? x) + (exn-message x) + (format "~s" x)))))))]) + (parameterize ([current-alist-separator-mode 'amp]) + (call/input-url + url + (case-lambda + [(x) (post-pure-port x post-data)] + [(x y) (post-pure-port x post-data y)]) + (lambda (port) + (send response-text lock #f) + (send response-text erase) + (render-html-to-text port response-text #t #f) + (send response-text lock #t)))) + (queue-callback + (lambda () + (send response-abort enable #f) + (send response-reset enable #t) + (send response-close enable #t) + (set! cancel-kill-thread #f) + (send bug-frame set-ok-to-close #t) + (send response-close focus)))))))]) + (set! cancel-kill-thread http-thread) + (send response-abort enable #t) + (switch-to-response-view))) + + (define (get-strings canvas) + (let ([t (send canvas get-editor)]) + (let loop ([n 0]) + (cond + [(> n (send t last-paragraph)) null] + [else (cons (send t get-text + (send t paragraph-start-position n) + (send t paragraph-end-position n)) + (loop (+ n 1)))])))) + + (define (sanity-checking) + (let ([no-value? + (lambda (f) + (cond + [(is-a? f editor-canvas%) + (= 0 (send (send f get-editor) last-position))] + [else (string=? "" (send f get-value))]))]) + (let/ec done-checking + (for-each + (lambda (field field-name) + (when (no-value? field) + (message-box (string-constant illegal-bug-report) + (format (string-constant pls-fill-in-field) field-name)) + (done-checking #f))) + (list name summary) + (list (string-constant bug-report-field-name) + (string-constant bug-report-field-summary))) + + (when (and (no-value? description) + (no-value? reproduce)) + (message-box (string-constant illegal-bug-report) + (string-constant pls-fill-in-either-description-or-reproduce)) + (done-checking #f)) + + (unless (regexp-match #rx"@" (or (preferences:get 'drscheme:email) "")) + (message-box (string-constant illegal-bug-report) + (string-constant malformed-email-address)) + (done-checking #f)) + (done-checking #t)))) + + (define (ok) + (when (sanity-checking) + (send-bug-report))) + + (define (cancel) + (cleanup-frame)) + + (define (cleanup-frame) + (send bug-frame close)) + + (define (directories-contents dirs) + (map (lambda (d) + (cons (path->string d) + (if (directory-exists? d) + (map path->string (directory-list d)) + '(non-existent-path)))) + dirs)) + + (define (split-by-directories dirs split-by) + (let ([res (append (map list (map path->string split-by)) '((*)))] + [dirs (map path->string dirs)]) + (for-each + (lambda (d) + (let* ([l (string-length d)] + [x (assf + (lambda (d2) + (or (eq? d2 '*) + (let ([l2 (string-length d2)]) + (and (< l2 l) (equal? d2 (substring d 0 l2)) + (member (string-ref d l2) '(#\/ #\\)))))) + res)]) + (append x (list (if (string? (car x)) + (substring d (add1 (string-length (car x)))) + d))))) + dirs) + (filter (lambda (x) (pair? (cdr x))) res))) + + (send response-ec allow-tab-exit #t) + + (send severity set-selection 1) + (send version set-value (format "~a" (version:version))) + + (send environment set-value + (format "~a ~s (~a) (get-display-depth) = ~a" + (system-type) + (system-type 'machine) + (system-library-subpath) + (get-display-depth))) + + (send (send collections get-editor) + insert + (format "~s" (directories-contents (get-collects-search-dirs)))) + + (send human-language set-value (format "~a" (this-language))) + (send memory-use set-value (format "~a" (current-memory-use))) + + (send (send collections get-editor) auto-wrap #t) + (send (send docs-installed get-editor) auto-wrap #t) + + ;; Currently, the help-menu is left empty + (frame:remove-empty-menus bug-frame) + + (align-labels) + (switch-to-compose-view) + + (send (send docs-installed get-editor) insert + (format "~s" (split-by-directories (find-doc-directories) + (get-doc-search-dirs)))) + + (send bug-frame show #t)) + + (define (ask-yes-or-no title msg parent) + (gui-utils:get-choice msg + (string-constant yes) + (string-constant no) + title + #f + parent))) diff --git a/collects/help/help-desk-urls.ss b/collects/help/help-desk-urls.ss new file mode 100644 index 0000000000..df14cb4c0b --- /dev/null +++ b/collects/help/help-desk-urls.ss @@ -0,0 +1,3 @@ +(module help-desk-urls mzscheme + (require "servlets/private/url.ss") + (provide (all-from "servlets/private/url.ss"))) \ No newline at end of file diff --git a/collects/help/private/buginfo.ss b/collects/help/private/buginfo.ss new file mode 100644 index 0000000000..010524c632 --- /dev/null +++ b/collects/help/private/buginfo.ss @@ -0,0 +1,21 @@ +(module buginfo mzscheme + + (provide set-bug-report-info! + get-bug-report-infos + bri-label + bri-value) + + (define-struct bri (label get-value)) + (define (bri-value bri) ((bri-get-value bri))) + + ; update with symbol/string assoc list + (define bug-report-infos null) + + (define (set-bug-report-info! str thunk) + (set! bug-report-infos (cons (make-bri str thunk) bug-report-infos))) + + (define (get-bug-report-infos) bug-report-infos)) + + + + diff --git a/collects/help/private/colldocs.ss b/collects/help/private/colldocs.ss new file mode 100644 index 0000000000..f2ccee00ce --- /dev/null +++ b/collects/help/private/colldocs.ss @@ -0,0 +1,65 @@ +(module colldocs mzscheme + (require (lib "list.ss") + (lib "getinfo.ss" "setup") + (lib "contract.ss")) + + ;; find-doc-directory-records : -> (list-of directory-record) + ;; Returns directory records containing doc.txt files, sorted first + ;; by lib/planet, then by path. + (define (find-doc-directory-records) + (define allrecs + (find-relevant-directory-records '(doc.txt) 'all-available)) + (define (recbytes (directory-record-path a)) + (path->bytes (directory-record-path b)))) + (define (librec? dirrec) + (let ([spec (directory-record-spec dirrec)]) + (and (pair? spec) (eq? (car spec) 'lib)))) + (append (sort (filter librec? allrecs) rec (values (list-of (list string path)) (list-of string)) + ;; Returns two lists having equal length. Each item in the first list + ;; contains a list containing a string (the directory) and a path (to + ;; the doc.txt file). The second list contains the corresponding descriptive + ;; names. + (define (colldocs) + (let loop ([dirrecs (find-doc-directory-records)] + [docs null] + [names null]) + (cond + [(null? dirrecs) (values (reverse docs) (reverse names))] + [else + (let* ([dirrec (car dirrecs)] + [dir (directory-record-path dirrec)] + [info-proc (get-info/full dir)]) + (if info-proc + (let ([doc.txt-path (info-proc 'doc.txt (lambda () #f))] + [name (info-proc 'name (lambda () #f))]) + (if (and (path-string? doc.txt-path) + (string? name)) + (loop (cdr dirrecs) + (cons (list dir (string->path doc.txt-path)) + docs) + (cons (pleasant-name name dirrec) + names)) + (loop (cdr dirrecs) docs names))) + (loop (cdr dirrecs) docs names)))]))) + + ;; pleasant-name : string directory-record -> string + ;; Generates a descriptive name for the collection/package. + (define (pleasant-name name dirrec) + (let ([spec (directory-record-spec dirrec)]) + (if (and (pair? spec) (list? spec)) + (case (car spec) + ((lib) (format "~a collection" name)) + ((planet) (format "~a package ~s" + name + `(,@(cdr spec) + ,(directory-record-maj dirrec) + ,(directory-record-min dirrec))))) + name))) + + (provide/contract + [colldocs (-> (values (listof (list/c path? path?)) + (listof string?)))])) diff --git a/collects/help/private/docpos.ss b/collects/help/private/docpos.ss new file mode 100644 index 0000000000..364ad70e67 --- /dev/null +++ b/collects/help/private/docpos.ss @@ -0,0 +1,65 @@ +(module docpos mzscheme + (require (lib "list.ss") + (lib "contract.ss")) + + ;; Define an order on the standard docs. + (define (standard-html-doc-position d) + (let ([str (path->string d)]) + (if (equal? str "help") + -1 + (let ([line (assoc str docs-and-positions)]) + (if line + (caddr line) + 100))))) + + + ;; (listof (list string string number)) + ;; the first string is the collection name + ;; the second string is the title of the the manual + ;; the number determines the sorting order for the manuals in the manuals page + (define docs-and-positions + `(("r5rs" "Revised^5 Report on the Algorithmic Language Scheme" -50) + ("mzscheme" "PLT MzScheme: Language Manual" -49) + ("mred" "PLT MrEd: Graphical Toolbox Manual" -48) + + ("tour" "A Brief Tour of DrScheme" 0) + ("drscheme" "PLT DrScheme: Programming Environment Manual" 1) + + ("srfi" "SRFI documents inside PLT" 3) + + ("mzlib" "PLT MzLib: Libraries Manual" 5) + ("misclib" "PLT Miscellaneous Libraries: Reference Manual" 6) + ("mrlib" "PLT MrLib: Graphical Libraries Manual" 7) + ("framework" "PLT Framework: GUI Application Framework" 8) + + ("mzc" "PLT mzc: MzScheme Compiler Manual" 10) + ("foreign" "PLT Foreign Interface Manual" 10) + + ("tools" "PLT Tools: DrScheme Extension Manual" 30) + ("insidemz" "Inside PLT MzScheme" 50) + + ("web-server" "Web Server Manual" 60) + ("swindle" "Swindle Manual" 61) + ("plot" "PLoT Manual" 62) + + ("t-y-scheme" "Teach Yourself Scheme in Fixnum Days" 100) + ("tex2page" "TeX2page" 101) + + ("beginning" "Beginning Student Language" 200) + ("beginning-abbr" "Beginning Student with List Abbreviations Language" 201) + ("intermediate" "Intermediate Student Language" 202) + ("intermediate-lambda" "Intermediate Student with Lambda Language" 203) + ("advanced" "Advanced Student Language" 204) + ("teachpack" "Teachpacks for How to Design Programs" 205) + ("teachpack-htdc" "Teachpacks for How to Design Classes" 206) + + ("profj-beginner" "ProfessorJ Beginner Language" 210) + ("profj-intermediate" "ProfessorJ Intermediate Language" 211) + ("profj-intermediate-access" "ProfessorJ Intermediate + access Language" 212) + ("profj-advanced" "ProfessorJ Advanced Language" 213))) + + (define known-docs (map (lambda (x) (cons (string->path (car x)) (cadr x))) docs-and-positions)) + + (provide/contract + [standard-html-doc-position (path? . -> . number?)] + [known-docs (listof (cons/c path? string?))])) diff --git a/collects/help/private/finddoc.ss b/collects/help/private/finddoc.ss new file mode 100644 index 0000000000..b45415b59e --- /dev/null +++ b/collects/help/private/finddoc.ss @@ -0,0 +1,79 @@ +(module finddoc mzscheme + (require (lib "dirs.ss" "setup") + (lib "match.ss") + "path.ss" + "get-help-url.ss") + + (provide finddoc + finddoc-page + finddoc-page-anchor + find-doc-directory) + + ;; Creates a "file:" link into the indicated manual. + ;; The link doesn't go to a particular anchor, + ;; because "file:" does not support that. + (define (finddoc manual index-key label) + (match (lookup manual index-key label) + [(docdir index-key filename anchor title) + `(a ((href ,(string-append + "file:" (build-path docdir filename)))) + ,label)] + [m m])) + + ; finddoc-page-help : string string boolean -> string + ; return url to the page where index-key is in manual, + ; optionally append an anchor + (define (finddoc-page-help manual index-key anchor?) + (match (lookup manual index-key "dummy") + [(docdir index-key filename anchor title) + (cond + [(servlet-path? (string->path filename)) + (string-append + filename (if anchor? (string-append "#" anchor) ""))] + [else + (get-help-url (build-path docdir filename) anchor)])] + [_ (error (format "Error finding index \"~a\" in manual \"~a\"" + index-key manual))])) + + ; finddoc-page : string string -> string + ; returns path for use by PLT Web server + ; path is of form /doc/manual/page, or + ; /servlet/ + (define (finddoc-page manual index-key) + (finddoc-page-help manual index-key #f)) + + ; finddoc-page-anchor : string string -> string + ; returns path (with anchor) for use by PLT Web server + ; path is of form /doc/manual/page#anchor, or + ; /servlet/#anchor + (define (finddoc-page-anchor manual index-key) + (finddoc-page-help manual index-key #t)) + + (define ht (make-hash-table)) + + ;; returns (list docdir index-key filename anchor title) + ;; or throws an error + (define (lookup manual index-key label) + (let* ([key (string->symbol manual)] + [docdir (find-doc-directory manual)] + [l (hash-table-get ht key + (lambda () + (let ([f (and docdir (build-path docdir "hdindex"))]) + (if (and f (file-exists? f)) + (let ([l (with-input-from-file f read)]) + (hash-table-put! ht key l) + l) + (error 'finddoc "manual index ~s not installed" manual)))))] + [m (assoc index-key l)]) + (if m + (cons docdir m) + (error 'finddoc "index key ~s not found in manual ~s" index-key manual)))) + + ;; finds the full path of the doc directory, if one exists + ;; input is just the short name of the directory (as a path) + (define (find-doc-directory doc) + (ormap (lambda (d) + (let ([p (build-path d doc)]) + (and (directory-exists? p) + p))) + (get-doc-search-dirs)))) diff --git a/collects/help/private/get-help-url.ss b/collects/help/private/get-help-url.ss new file mode 100644 index 0000000000..497b8c2c53 --- /dev/null +++ b/collects/help/private/get-help-url.ss @@ -0,0 +1,68 @@ +(module get-help-url mzscheme + + #| Library responsible for turning a path on disk into a URL the help desk can use |# + (require (lib "file.ss") + "internal-hp.ss" + (lib "contract.ss") + (lib "etc.ss") + (lib "config.ss" "planet") + (lib "dirs.ss" "setup")) + + ; given a manual path, convert to absolute Web path + ; manual path is an anchored path to a doc manual, never a servlet + (define get-help-url + (opt-lambda (manual-path [anchor #f]) + (let ([segments (explode-path (normalize-path manual-path))]) + (let loop ([candidates manual-path-candidates]) + (cond + ;; shouldn't happen, unless documentation is outside + ;; the set of doc dirs: + [(null? candidates) "/cannot-find-docs.html"] + [else + (let ([candidate (car candidates)]) + (cond + [(subpath/tail (car candidate) segments) + => + (λ (l-o-path) + ((cadr candidate) l-o-path anchor))] + [else + (loop (cdr candidates))]))]))))) + + (define manual-path-candidates '()) + (define (maybe-add-candidate candidate host) + (with-handlers ([exn:fail? void]) + (set! manual-path-candidates + (cons (list (explode-path (normalize-path candidate)) + (λ (segments anchor) + (format "http://~a:~a/servlets/static.ss/~a~a~a" + internal-host + (internal-port) + host + (apply string-append (map (λ (x) (format "/~a" (path->string x))) + segments)) + (if anchor + (string-append "#" anchor) + "")))) + manual-path-candidates)))) + + ;; Add doc dirs later, so that they take precedence: + (maybe-add-candidate (PLANET-DIR) planet-host) + (for-each (λ (dir host) (maybe-add-candidate dir host)) + (append collects-dirs doc-dirs) + (append collects-hosts doc-hosts)) + + (define (subpath/tail short long) + (let loop ([short short] + [long long]) + (cond + [(null? short) long] + [(null? long) #f] + [(equal? (car short) (car long)) + (loop (cdr short) (cdr long))] + [else #f]))) + + (provide/contract (get-help-url + (opt-> + ((or/c path? path-string?)) + (string?) + string?)))) diff --git a/collects/help/private/internal-hp.ss b/collects/help/private/internal-hp.ss new file mode 100644 index 0000000000..4ae2ffb15c --- /dev/null +++ b/collects/help/private/internal-hp.ss @@ -0,0 +1,54 @@ +(module internal-hp mzscheme + (require (lib "dirs.ss" "setup") + (lib "config.ss" "planet") + "options.ss") + (provide internal-port + is-internal-host? internal-host + collects-hosts collects-dirs + doc-hosts doc-dirs + planet-host) + + ;; Hostnames defined here should not exist as real machines + + ;; The general idea is that there's one "virtual" host for + ;; every filesystem tree that we need to access. + ;; (now we use static.ss/host/yadayda instead of the virtual + ;; host docX.localhost, but we still need to keep track of + ;; the file system roots) + ;; The "get-help-url.ss" library provides a function to + ;; convert a path into a suitable URL (i.e., a URL using + ;; the right virtual host). + ;; The "gui.ss" library performs a bit of extra URL + ;; processing at the last minute, sometimes switching + ;; a URL for a manual to a different host. (That's needed + ;; when cross-manual references are implemented as relative + ;; URLs.) + + (define internal-host "localhost") + + (define (is-internal-host? str) + (member str all-internal-hosts)) + + (define (generate-hosts prefix dirs) + (let loop ([dirs dirs][n 0]) + (if (null? dirs) + null + (cons (format "~a~a" prefix n) + (loop (cdr dirs) (add1 n)))))) + + (define planet-host "planet") + + (define collects-dirs + (get-collects-search-dirs)) + (define collects-hosts + (generate-hosts "collects" collects-dirs)) + + (define doc-dirs + (get-doc-search-dirs)) + (define doc-hosts + (generate-hosts "doc" doc-dirs)) + + (define all-internal-hosts + (append (list internal-host planet-host) + collects-hosts + doc-hosts))) diff --git a/collects/help/private/manuals.ss b/collects/help/private/manuals.ss new file mode 100644 index 0000000000..f0d08eeca5 --- /dev/null +++ b/collects/help/private/manuals.ss @@ -0,0 +1,380 @@ +(module manuals mzscheme + (require (lib "list.ss") + (lib "date.ss") + (lib "string-constant.ss" "string-constants") + (lib "xml.ss" "xml") + (lib "contract.ss") + (lib "getinfo.ss" "setup") + (lib "uri-codec.ss" "net") + (lib "dirs.ss" "setup") + (lib "match.ss") + "finddoc.ss" + "colldocs.ss" + "docpos.ss" + "standard-urls.ss" + "get-help-url.ss" + "../servlets/private/util.ss") + + ;; type sec = (make-sec name regexp (listof regexp)) + (define-struct sec (name reg seps)) + + ;; sections : (listof sec) + ;; determines the section breakdown for the manuals + ;; elements in the outer list: + ;; string : name of section + ;; predicate : determines if a manual is in the section (based on its title) + ;; breaks -- where to insert newlines + (define sections + (list (make-sec "Getting started" + #rx"(Tour)|(Teach Yourself)" + '()) + (make-sec "Languages" + #rx"Language|MrEd" + '(#rx"Beginning Student" #rx"ProfessorJ Beginner")) + (make-sec "Tools" #rx"PLT DrScheme|PLT mzc|TeX2page|Web Server|PLoT" '()) + (make-sec "Libraries" #rx"SRFI|MzLib|Framework|PLT Miscellaneous|Teachpack|Swindle" '()) + (make-sec "Writing extensions" #rx"Tools|Inside|Foreign" '()) + (make-sec "Other" #rx"" '()))) + + ; main-manual-page : string -> xexpr + ; return link to main manual page of a doc collection, like "mred" + (define (main-manual-page manual) + (let* ([entry (assoc (string->path manual) known-docs)] + [name (or (and entry (cdr entry)) + manual)] + [doc-dir (find-doc-directory manual)]) + (if doc-dir + (let ([href (get-help-url doc-dir)]) + `(A ((HREF ,href)) ,name)) + name))) + + ; manual-entry: string string string -> xexpr + ; man is manual name + ; ndx is index into the manual + ; txt is the link text + (define (manual-entry man ndx txt) + (with-handlers ([exn:fail? + ;; warning: if the index file isn't present, this page + (lambda (x) + `(font ((color "red")) ,txt " [" ,(exn-message x) "]"))]) + `(A ((HREF ,(finddoc-page man ndx))) ,txt))) + + (define (basename path) + (let-values ([(dir name dir?) (split-path path)]) name)) + + (define (find-doc-names) + (let* ([dirs (find-doc-directories)] + [installed (map basename dirs)] + [uninstalled (filter (lambda (x) (not (member (car x) installed))) + known-docs)]) + (append (map (lambda (short-name long-name) + (cons short-name (get-doc-name long-name))) + installed dirs) + uninstalled))) + + ;; find-doc-directories : -> (listof path) + ;; constructs a sorted list of directories where documentation may reside. + (define (find-doc-directories) + (let ([unsorted (append (find-info.ss-doc-directories) + (find-doc-directories-in-toplevel-docs))]) + (sort unsorted compare-docs))) + + (define (find-info.ss-doc-directories) + (let ([dirs (find-relevant-directories '(html-docs) 'all-available)]) + (let loop ([dirs dirs]) + (cond + [(null? dirs) null] + [else (let* ([dir (car dirs)] + [info (get-info/full dir)]) + (cond + [info + (let ([html-doc-paths (info 'html-docs (lambda () #f))]) + (cond + [(and (list? html-doc-paths) + (andmap path-string? html-doc-paths)) + (let ([candidates (map (lambda (x) (build-path dir x)) html-doc-paths)]) + (for-each (λ (c) + (unless (directory-exists? c) + (fprintf (current-error-port) + "found reference to ~a in html-docs for ~a, but it is not a directory\n" + (path->string c) + (path->string dir)))) + candidates) + (append (filter directory-exists? candidates) + (loop (cdr dirs))))] + [else + (loop (cdr dirs))]))] + [else (loop (cdr dirs))]))])))) + + (define (find-doc-directories-in-toplevel-docs) + (apply append + (map (lambda (docs-path) + (filter directory-exists? + (map (lambda (doc-path) + (build-path docs-path doc-path)) + (if (directory-exists? docs-path) + (filter (lambda (x) + (not (member (path->string x) + '(".svn" "CVS")))) + (directory-list docs-path)) + '())))) + (get-doc-search-dirs)))) + + (define (find-manuals) + (let* ([docs (sort (filter get-index-file (find-doc-directories)) + compare-docs)] + [names (map get-doc-name docs)] + [names+paths (map cons names docs)]) + (let-values ([(collections-doc-files collection-names) (colldocs)]) + `((H1 "Installed Manuals") + ,@(if (repos-or-nightly-build?) + (list + '(b "Subversion: ") + `(a ((mzscheme + ,(to-string/escape-quotes + `((dynamic-require '(lib "refresh-manuals.ss" "help") 'refresh-manuals))))) + ,(string-constant plt:hd:refresh-all-manuals)) + 'nbsp 'nbsp + `(a ((href ,flush-manuals-url)) "flush index and keyword cache") + '(br)) + (list)) + ,@(build-known-manuals names+paths) + (h3 "Doc.txt") + (ul ,@(map + (lambda (collection-doc-file name) + (let ([path (build-path (car collection-doc-file) (cadr collection-doc-file))]) + `(li ,(cond + [(file-exists? path) + `(a ((href ,(format "/servlets/doc-anchor.ss?file=~a&name=~a&caption=~a" + ;; escape colons and other junk + (uri-encode (path->string path)) + (uri-encode name) + (format "Documentation for ~a " name)))) + ,(format "~a " name))] + [else + `(font ((color "red")) + ,(format "~a: specified doc.txt file (~a) not found" + name path))])))) + collections-doc-files + collection-names)) + + ,@(let ([uninstalled (get-uninstalled docs)]) + (if (null? uninstalled) + '() + `((h3 "Uninstalled Manuals") + (ul ,@(map + (lambda (doc-pair) + (let* ([manual (car doc-pair)] + [name (cdr doc-pair)] + [manual-path (find-doc-directory manual)]) + `(li "Download and install " + (a ((mzscheme + ,(to-string/escape-quotes + `((dynamic-require '(lib "refresh-manuals.ss" "help") 'refresh-manuals) + (list (cons ((dynamic-require '(lib "refresh-manuals.ss" "help") 'bytes-to-path) + ,(path->bytes manual)) + ,name)))))) + ,name) + ,(if (and manual-path + (or (file-exists? (build-path manual-path "hdindex")) + (file-exists? (build-path manual-path "keywords")))) + " (index installed)" + "")))) + uninstalled))))))))) + + + ;; build-known-manuals : (listof (cons string[title] string[path])) -> (listof xexpr) + (define (build-known-manuals names+paths) + (let loop ([sections sections] + [manuals names+paths]) + (cond + [(null? sections) null] + [else + (let* ([section (car sections)] + [in (filter (lambda (x) (regexp-match (sec-reg section) + (car x))) + manuals)] + [out (filter (lambda (x) (not (regexp-match (sec-reg section) + (car x)))) + manuals)]) + (append (build-known-section section in) + (loop (cdr sections) out)))]))) + + ;; build-known-section : sec (listof (cons string[title] string[path]))) -> (listof xexpr) + (define (build-known-section sec names+paths) + (if (null? names+paths) + '() + `((h3 ,(sec-name sec)) + (ul ,@(map (match-lambda + ["

" '(p)] + [(title . path) (mk-link path title)]) + (let loop ([breaks (sec-seps sec)] + [names+paths names+paths]) + (cond + [(null? breaks) names+paths] + [else + (let ([break (car breaks)]) + (loop (cdr breaks) + (break-between (car breaks) names+paths)))]))))))) + + ;; break-between : regexp + ;; (listof (union string (cons string string))) + ;; -> (listof (union string (cons string string))) + ;; adds the para-mark string into the list at the first place + ;; that the regexp fails to match (not counting other para-marks + ;; in the list) + (define (break-between re l) + (let ([para-mark "

"]) + (let loop ([l l]) + (cond + [(null? l) null] + [else + (let ([fst (car l)]) + (cond + [(pair? fst) + (let ([name (car fst)]) + (if (regexp-match re name) + (cons para-mark l) + (cons fst (loop (cdr l)))))] + [else (cons fst (loop (cdr l)))]))])))) + + + ;; mk-link : string string -> xexpr + (define (mk-link doc-path name) + (let* ([manual-name (basename doc-path)] + [index-file (get-index-file doc-path)]) + `(li (a ((href ,(get-help-url (build-path doc-path index-file)))) + ,name) + ,@(cond + [(and (repos-or-nightly-build?) + (file-exists? (build-path doc-path index-file))) + `((br) + 'nbsp + 'nbsp + (font ((size "-1")) + ,@(if (is-known-doc? doc-path) + (list + "[" + `(a ((mzscheme + ,(to-string/escape-quotes + `((dynamic-require '(lib "refresh-manuals.ss" "help") 'refresh-manuals) + (list (cons ((dynamic-require '(lib "refresh-manuals.ss" "help") 'bytes-to-path) + ,(path->bytes manual-name)) + ,name)))))) + ,(string-constant plt:hd:refresh)) + "]" 'nbsp) + (list))))] + [else + (list + (format (string-constant plt:hd:manual-installed-date) + (date->string + (seconds->date + (file-or-directory-modify-seconds + (build-path doc-path index-file))))))])))) + + (define (to-string/escape-quotes exp) + (regexp-replace* #rx"\"" (format "~s" exp) "|")) + + ;; get-doc-name : path -> string + (define cached-doc-names (make-hash-table 'equal)) + (define (get-doc-name doc-dir) + (hash-table-get cached-doc-names doc-dir + (lambda () + (let ([res (compute-doc-name doc-dir)]) + (hash-table-put! cached-doc-names doc-dir res) + res)))) + + ;; compute-doc-name : path -> string[title of manual] + ;; gets the title either from the known docs list, by parsing the + ;; html, or if both those fail, by using the name of the directory + ;; Special-cases the help collection. It's not a known doc directory + ;; per se, so it won't appear in known-docs, but its name is always + ;; the same. + (define (compute-doc-name doc-dir) + (let ([doc-short-dir-name (basename doc-dir)]) + (cond + [(equal? (string->path "help") doc-short-dir-name) "PLT Help Desk"] + [(get-known-doc-name doc-dir) => values] + [else (let* ([main-file (get-index-file doc-dir)] + [m (and main-file + (call-with-input-file (build-path doc-dir main-file) + (lambda (inp) (regexp-match re:title inp))))]) + (if m + (bytes->string/utf-8 (cadr m)) + (path->string doc-short-dir-name)))]))) + (define re:title + #rx"<[tT][iI][tT][lL][eE]>[ \t\r\n]*(.*?)[ \t\r\n]*") + + ;; is-known-doc? : string[path] -> boolean + (define (is-known-doc? doc-path) + (and (assoc (basename doc-path) known-docs) #t)) + + ;; get-known-doc-name : string[full-path] -> (union string #f) + (define (get-known-doc-name doc-path) + (cond [(assoc (basename doc-path) known-docs) => cdr] [else #f])) + + ;; get-uninstalled : (listof path) -> (listof (cons path string[docs-name])) + (define (get-uninstalled docs) + (let ([ht (make-hash-table 'equal)]) + (for-each (lambda (known-doc) + (hash-table-put! ht + (car known-doc) + (cdr known-doc))) + known-docs) + (for-each (lambda (doc) (hash-table-remove! ht (basename doc))) docs) + (sort (hash-table-map ht cons) + (λ (a b) (compare-docs (car a) (car b)))))) + + (define (compare-docs a b) + (let ([ap (standard-html-doc-position (basename a))] + [bp (standard-html-doc-position (basename b))]) + (cond [(= ap bp) (stringstring a) (path->string b))] + [else (< ap bp)]))) + + ;; get-manual-index : string -> html + (define (get-manual-index manual-dirname) (get-help-url (build-path (find-doc-dir) manual-dirname))) + + ;; get-index-file : path -> (union #f path) + ;; returns the name of the main file, if one can be found + (define (get-index-file doc-dir) + (cond + [(file-exists? (build-path doc-dir "index.htm")) + (build-path "index.htm")] + [(file-exists? (build-path doc-dir "index.html")) + (build-path "index.html")] + [(tex2page-detected doc-dir) + => + (lambda (x) x)] + [else #f])) + + ;; tex2page-detected : string -> (union #f string) + (define (tex2page-detected dir) + (let loop ([contents (directory-list dir)]) + (cond + [(null? contents) #f] + [else (let* ([file (car contents)] + [m (regexp-match #rx#"(.*)-Z-H-1.html" (path->bytes file))]) + (or (and m + (file-exists? (build-path dir file)) + (let ([index-file + (bytes->path + (bytes-append (cadr m) #".html"))]) + (if (file-exists? (build-path dir index-file)) + index-file + #f))) + (loop (cdr contents))))]))) + + + (provide find-manuals + main-manual-page + finddoc + finddoc-page-anchor) + + (provide/contract [manual-entry (string? string? xexpr? . -> . xexpr?)] + [finddoc-page (string? string? . -> . string?)] + [get-doc-name (path? . -> . string?)] + [find-doc-directories (-> (listof path?))] + [find-doc-directory (path? . -> . (or/c false/c path?))] + [find-doc-names (-> (listof (cons/c path? string?)))] + [get-manual-index (-> string? string?)] + [get-index-file (path? . -> . (or/c false/c path?))])) diff --git a/collects/help/private/options.ss b/collects/help/private/options.ss new file mode 100644 index 0000000000..d3e54d264a --- /dev/null +++ b/collects/help/private/options.ss @@ -0,0 +1,22 @@ +(module options mzscheme + + ;; This module provides configuration options that are shared + ;; between servlets and the web-server. (Mostly to allow + ;; configuration as an application or as a standalone server.) + + (provide helpdesk-platform internal-port) + + ;; internal browser or external browser? + ;; (used to produce simpler html for the internal browser) + (define helpdesk-platform + (make-parameter + 'internal-browser-simple ; main page only + ;; 'internal-browser ; menu + main page + ;; 'external-browser + )) + + ;; Port for the server to listen on + ;; (relevant only for a standalone server) + (define internal-port (make-parameter 8012)) + + ) diff --git a/collects/help/private/path.ss b/collects/help/private/path.ss new file mode 100644 index 0000000000..f842a3ae08 --- /dev/null +++ b/collects/help/private/path.ss @@ -0,0 +1,10 @@ +(module path mzscheme + (require (lib "contract.ss")) + (define (servlet-path? path) + (if (regexp-match #rx#"^/servlets/" + (path->bytes path)) + #t + #f)) + (provide/contract + [servlet-path? (path? . -> . boolean?)])) + diff --git a/collects/help/private/standard-urls.ss b/collects/help/private/standard-urls.ss new file mode 100644 index 0000000000..33a4654597 --- /dev/null +++ b/collects/help/private/standard-urls.ss @@ -0,0 +1,134 @@ +(module standard-urls mzscheme + (require (lib "uri-codec.ss" "net") + (lib "dirs.ss" "setup") + (lib "contract.ss") + (lib "config.ss" "planet") + (lib "help-desk-urls.ss" "help") + "../servlets/private/util.ss" + "internal-hp.ss" + "get-help-url.ss") + + (provide home-page-url host+dirs) + + (define (search-type? x) + (member x '("keyword" "keyword-index" "keyword-index-text"))) + + (define (search-how? x) + (member x '("exact-match" "containing-match" "regexp-match"))) + + (define (base-docs-url) + (if (repos-or-nightly-build?) + "http://pre.plt-scheme.org/docs" + (string-append "http://download.plt-scheme.org/doc/" (version)))) + + (define (make-docs-plt-url manual-name) + (format "~a/bundles/~a-doc.plt" (base-docs-url) manual-name)) + + (define (make-docs-html-url manual-name) + (format "~a/html/~a/index.htm" (base-docs-url) manual-name)) + + (define (prefix-with-server suffix) + (format "http://~a:~a~a" internal-host (internal-port) suffix)) + + (define results-url-prefix (format "http://~a:~a/servlets/results.ss?" internal-host (internal-port))) + (define flush-manuals-path "/servlets/results.ss?flush=yes") + (define flush-manuals-url (format "http://~a:~a~a" internal-host (internal-port) flush-manuals-path)) + + + (define relative-results-url-prefix "/servlets/results.ss?") + + (define home-page-url (format "http://~a:~a/servlets/home.ss" internal-host (internal-port))) + + (define (make-missing-manual-url coll name link) + (format "http://~a:~a/servlets/missing-manual.ss?manual=~a&name=~a&link=~a" + internal-host + (internal-port) + coll + (uri-encode name) + (uri-encode link))) + + (define (make-relative-results-url search-string search-type match-type lucky? manuals doc.txt? lang-name) + (string-append + relative-results-url-prefix + (make-results-url-args search-string search-type match-type lucky? manuals doc.txt? lang-name))) + + (define (make-results-url search-string search-type match-type lucky? manuals doc.txt? lang-name) + (string-append + results-url-prefix + (make-results-url-args search-string search-type match-type lucky? manuals doc.txt? lang-name))) + + (define (make-results-url-args search-string search-type match-type lucky? manuals doc.txt? language-name) + (let ([start + (format + (string-append "search-string=~a&" + "search-type=~a&" + "match-type=~a&" + "lucky=~a&" + "manuals=~a&" + "doctxt=~a") + (uri-encode search-string) + search-type + match-type + (if lucky? "true" "false") + (uri-encode (format "~s" (map path->bytes manuals))) + (if doc.txt? "true" "false"))]) + (if language-name + (string-append start (format "&langname=~a" (uri-encode language-name))) + start))) + + ; sym, string assoc list + (define hd-locations + `((hd-tour ,(format "~a/index.html" (get-help-url (build-path (find-doc-dir) "tour")))) + (release-notes ,url-helpdesk-release-notes) + (plt-license ,url-helpdesk-license) + (front-page ,url-helpdesk-home))) + + (define hd-location-syms (map car hd-locations)) + + (define (get-hd-location sym) + ; the assq is guarded by the contract + (cadr (assq sym hd-locations))) + + ; host+dirs : (list (cons host-string dir-path)) + ; association between internal (in normal Helpdesk also virtual) + ; hosts and their corresponding file root. + (define host+dirs + (map cons + (append collects-hosts doc-hosts) + (append collects-dirs doc-dirs))) + + (define (host+file->path host file-path) + (cond [(assoc host host+dirs) + => (lambda (internal-host+path) + (let ([path (cdr internal-host+path)]) + (build-path path file-path)))] + [(equal? host "planet") + (build-path (PLANET-DIR) file-path)] + [else #f])) + + (provide host+file->path) + (provide search-type? search-how?) + (provide/contract + (make-relative-results-url (string? + search-type? + search-how? + any/c + (listof path?) + any/c + (or/c false/c string?) . -> . string?)) + (make-results-url (string? + search-type? search-how? any/c + (listof path?) + any/c + (or/c false/c string?) + . -> . + string?)) + (flush-manuals-url string?) + (flush-manuals-path string?) + (make-missing-manual-url (string? string? string? . -> . string?)) + (get-hd-location ((lambda (sym) (memq sym hd-location-syms)) + . -> . + string?)) + [prefix-with-server (string? . -> . string?)] + [make-docs-plt-url (string? . -> . string?)] + [make-docs-html-url (string? . -> . string?)])) diff --git a/collects/help/servlets/private/url.ss b/collects/help/servlets/private/url.ss new file mode 100644 index 0000000000..27a552a452 --- /dev/null +++ b/collects/help/servlets/private/url.ss @@ -0,0 +1,83 @@ +(module url mzscheme + (require "../../private/internal-hp.ss") + + (provide (all-defined)) + + (define url-helpdesk-root + (format "http://~a:~a/servlets/" internal-host (internal-port))) + + (define url-helpdesk-home (string-append url-helpdesk-root "home.ss")) + (define url-helpdesk-results (string-append url-helpdesk-root "results.ss")) + (define url-helpdesk-master-index (string-append url-helpdesk-root "master-index.ss")) + + + (define (url-home-subpage subpage-str) + (string-append url-helpdesk-home "?subpage=" subpage-str)) + + (define (version-major) + ; TODO: Fix this + (cond [(regexp-match #px"^(\\d+).*$" (version)) + => cadr] + [else "352"])) + + (define (url-manual-on-doc-server manual) + (format "http://download.plt-scheme.org/doc/~a/html/~a/" + (version-major) manual)) + + (define (url-static doc manual path) + (format "~astatic.ss/~a/~a/~a" + url-helpdesk-root doc manual path)) + + (define url-external-announcement-list-archive "http://list.cs.brown.edu/pipermail/plt-announce/") + (define url-external-discussion-list-archive "http://list.cs.brown.edu/pipermail/plt-scheme/") + (define url-external-discussion-list-archive-old "http://www.cs.utah.edu/plt/mailarch/") + (define url-external-mailing-list-subscription "http://www.plt-scheme.org/maillist/") + (define url-external-mrflow "http://www.plt-scheme.org/software/mrflow/") + (define url-external-mrspidey "http://www.plt-scheme.org/software/mrspidey/") + (define url-external-mysterx "http://www.plt-scheme.org/software/mysterx/") + (define url-external-mzcom "http://www.plt-scheme.org/software/mzcom/") + (define url-external-send-bug-report "http://bugs.plt-scheme.org/") + (define url-external-tour-of-drscheme "http://www.plt-scheme.org/software/drscheme/tour/") + (define url-external-planet "http://planet.plt-scheme.org/") + (define url-external-srpersist "http://www.plt-scheme.org/software/srpersist/") + + (define url-helpdesk-acknowledge (url-home-subpage "acknowledge")) + (define url-helpdesk-batch (url-home-subpage "batch")) + (define url-helpdesk-books (url-home-subpage "books")) + (define url-helpdesk-cgi (url-home-subpage "cgi")) + (define url-helpdesk-databases (url-home-subpage "databases")) + (define url-helpdesk-documentation (url-home-subpage "documentation")) + (define url-helpdesk-drscheme (url-home-subpage "drscheme")) + (define url-helpdesk-drscheme-faq (url-static "doc1" "drscheme" "drscheme-Z-H-5.html#node_chap_5")) + (define url-helpdesk-drscheme-manual (url-static "doc1" "drscheme" "index.htm")) + (define url-helpdesk-faq (url-home-subpage "faq")) + (define url-helpdesk-graphics (url-home-subpage "graphics")) + (define url-helpdesk-help (url-home-subpage "help")) + (define url-helpdesk-how-to-search (url-home-subpage "how-to-search")) + (define url-helpdesk-interface-essentials (url-static "doc1" "drscheme" "drscheme-Z-H-2.html#node_chap_2")) + (define url-helpdesk-known-bugs (url-home-subpage "known-bugs")) + (define url-helpdesk-languages (url-home-subpage "languages")) + (define url-helpdesk-libraries (url-home-subpage "libraries")) + (define url-helpdesk-license (url-home-subpage "license")) + (define url-helpdesk-manuals (url-home-subpage "manuals")) + (define url-helpdesk-mailing-lists (url-home-subpage "mailing-lists")) + (define url-helpdesk-mzlib (url-static "doc1" "mzlib" "mzlib.html")) + (define url-helpdesk-patches (url-home-subpage "patches")) + (define url-helpdesk-program-design (url-home-subpage "program-design")) + (define url-helpdesk-release (url-home-subpage "release")) + (define url-helpdesk-release-notes (url-home-subpage "release-notes")) + (define url-helpdesk-script (url-home-subpage "script")) + (define url-helpdesk-search (url-home-subpage "search")) + (define url-helpdesk-software (url-home-subpage "software")) + (define url-helpdesk-srpersist (url-home-subpage "srpersist")) + (define url-helpdesk-stand-alone (url-home-subpage "stand-alone")) + (define url-helpdesk-system (url-home-subpage "system")) + (define url-helpdesk-teachpacks (url-home-subpage "teachpacks")) + (define url-helpdesk-teachscheme (url-home-subpage "teachscheme")) + (define url-helpdesk-teachpacks-for-htdp (url-static "doc1" "teachpack" "index.html#HtDP")) + (define url-helpdesk-teachpacks-for-htdc (url-static "doc1" "teachpack-htdc" "index.html#HtDC")) + (define url-helpdesk-teach-yourself (url-static "doc1" "t-y-scheme" "index.htm")) + (define url-helpdesk-tour (url-home-subpage "tour")) + (define url-helpdesk-why-drscheme (url-home-subpage "why-drscheme")) + + ) diff --git a/collects/help/servlets/private/util.ss b/collects/help/servlets/private/util.ss new file mode 100644 index 0000000000..301316427c --- /dev/null +++ b/collects/help/servlets/private/util.ss @@ -0,0 +1,114 @@ +(module util mzscheme + (require (lib "file.ss") + (lib "list.ss") + (lib "xml.ss" "xml") + (lib "uri-codec.ss" "net") + (lib "string-constant.ss" "string-constants") + (lib "contract.ss")) + + ;; would be nice if this could use version:version from the framework. + (define (plt-version) + (let ([mz-version (version)] + [stamp-collection + (with-handlers ([exn:fail:filesystem? (lambda (exn) #f)]) + (collection-path "repos-time-stamp"))]) + (if (and stamp-collection + (file-exists? (build-path stamp-collection "stamp.ss"))) + (format "~a-svn~a" mz-version + (dynamic-require '(lib "repos-time-stamp/stamp.ss") 'stamp)) + mz-version))) + + (define home-page + `(a ([href "/servlets/home.ss"] [target "_top"]) + ,(string-constant plt:hd:home))) + + (define (get-pref/default pref default) + (get-preference pref (lambda () default))) + + (define (get-bool-pref/default pref default) + (let ([raw-pref (get-pref/default pref default)]) + (if (string=? raw-pref "false") #f #t))) + + (define (put-prefs names vals) + (put-preferences names vals)) + + (define search-height-default "85") + (define search-bg-default "lightsteelblue") + (define search-text-default "black") + (define search-link-default "darkblue") + + (define *the-highlight-color* "forestgreen") + + ;; string xexpr ... -> xexpr + (define (with-color color . s) + `(font ([color ,color]) ,@s)) + + ;; xexpr ... -> xexpr + (define (color-highlight . s) + (apply with-color *the-highlight-color* s)) + + (define repos-or-nightly-build? + (let ([helpdir (collection-path "help")]) + (lambda () + (or (directory-exists? (build-path helpdir ".svn")) + (directory-exists? (build-path helpdir "CVS")) + (with-handlers ([exn:fail:filesystem? (lambda (x) #f)]) + (collection-path "repos-time-stamp")))))) + + ; string string -> xexpr + (define (collection-doc-link coll txt) + (let ([coll-file (build-path (collection-path coll) "doc.txt")]) + (if (file-exists? coll-file) + `(a ((href + ,(format + "~a?file=~a&name=~a&caption=Documentation for the ~a collection" + "/servlets/doc-anchor.ss" + (uri-encode (path->string coll-file)) + coll + coll))) + ,txt) + ""))) + + ;; (listof string) -> string + ;; result is forward-slashed web path + ;; e.g. ("foo" "bar") -> "foo/bar" + (define (fold-into-web-path lst) + (foldr (lambda (s a) (if a (string-append s "/" a) s)) #f lst)) + + (define (format-collection-message s) + `(b ((style "color:green")) ,s)) + + (define (make-javascript . ss) + `(script ([language "Javascript"]) + ,(make-comment (apply string-append "\n" + (map (lambda (s) (string-append s "\n")) ss))))) + + (define (redir-javascript k-url) + (make-javascript "function redir() {" + (string-append " document.location.href=\"" k-url "\"") + "}")) + + (define (onload-redir secs) + (string-append "setTimeout(\"redir()\"," + (number->string (* secs 1000)) ")")) + + (provide/contract + [fold-into-web-path ((listof string?) . -> . string?)]) + + (provide get-pref/default + get-bool-pref/default + put-prefs + repos-or-nightly-build? + search-height-default + search-bg-default + search-text-default + search-link-default + color-highlight + with-color + collection-doc-link + home-page + format-collection-message + plt-version + make-javascript + redir-javascript + onload-redir))