0

My program allows users to select files to import into a database. Before now, it only allowed them to import one file at a time. Letting them import more than one file at a time is easy. But the problem I have is that if the database already contains a page with the same title, I want to display a warning and get confirmation before overwriting the version in the database with the one being imported.

Here's what I have so far. It largely follows what I was doing to import single files.

;; Handler for the POST "/import" route.
(defn- post-import-page
  "Import the file(s) specified in the upload dialog. Checks the page
  title of the import file against existing pages in the database. If
  a page of the same name already exists, asks for confirmation before
  importing."
  [{{file-info "file-info"
     referer   "referer"} :multipart-params :as req}]
  (let [file-vec (if (map? file-info)
                   (conj [] file-info)
                   file-info)]
    (doseq [fm file-vec]
      (let [file-name (:filename fm)
            import-map (files/load-markdown-from-file (:tempfile fm))
            page-title (get-in import-map [:meta :title])
            id-exists? (db/title->page-id page-title)]
        (if id-exists?
          (build-response
            (layout/compose-import-existing-page-warning
              import-map file-name referer) req)
          (do-the-import import-map file-name req))))))

This function imports any files that don't already exist in the database, but doesn't import anything that would overwrite an existing database entry with the same title. It never shows the warning page asking for confirmation either.

The warning page is constructed like this:

(defn compose-import-existing-page-warning
  "Return a page stating that a page with the same title already exists 
  in the wiki. Ask the user to proceed or cancel the import."
  [import-map file-name referer]
  (short-form-template
    [:div {:class "cwiki-form"}
     (form-to {:enctype      "multipart/form-data"
               :autocomplete "off"}
              [:post "proceed-with-import"]
              (hidden-field "import-map" import-map)
              (hidden-field "file-name" file-name)
              (hidden-field "referer" referer)
              [:p {:class "form-title"} "Page Already Exists"]
              [:div {:class "form-group"}
               [:p (str "A page with the title \"" (get-in import-map [:meta :title])
                        "\" already exists in the wiki.")]
               [:p (str "Click \"Proceed\" to delete the existing page and "
                        "replace it with the contents of the imported file.")]
               [:div {:class "button-bar-container"}
                (submit-button {:id    "proceed-with-import-button"
                                :class "form-button button-bar-item"}
                               "Proceed")
                [:input {:type      "button" :name "cancel-button"
                         :value     "Cancel"
                         :class     "form-button button-bar-item"
                         :autofocus "autofocus"
                         :onclick   "window.history.back();"}]]])]))

How would the program be paused in the middle of the doseq (or other looping function) to display the confirmation page and wait for the user to make a selection?

clartaq
  • 5,320
  • 3
  • 39
  • 49
  • 1
    Sounds like a good case for `core.async` and channels. Seperate the logic of uploading from checking if the file already exist. Have the uploader loop over a channel with files that are ready to be uploaded. Have a second queue of files that need to be confirmed first. Once confirmed, add them to the uploader channel – Jochen Bedersdorfer Apr 11 '20 at 18:42
  • 1
    Is this for a website? `go-loop` parks for input, but Back/Reload in the browser could get out of step with it. Anyway, next they might tell you "If we say No to anything, then don't insert any". The usual thing is to rewrite it as a non-interactive batch procedure that takes as input all the things it's allowed to do (and does all-or-nothing). For example, add a `:force` flag per item and return all outstanding conflicts. Multiple web POST handlers could use it - there are so many possible user interfaces (wizard, expert mode, JSON-web-service, etc). – Biped Phill Apr 12 '20 at 00:25
  • @BipedPhill: Yup, it's for a website. As you say, one option is just give the user a set of options before running in batch mode: Overwrite All, Overwrite None, etc. – clartaq Apr 12 '20 at 13:53
  • @JochenBedersdorfer: That sounds like a possibility. I'll look into `core.asynch` a bit more. That might allow me to keep things fully interactive. – clartaq Apr 12 '20 at 13:54

1 Answers1

-1

Just use read-line in the middle of your loop, and then an if to choose the branching you want. Here is a list of other documentation you may find useful, especially the Clojure CheatSheet.

Alan Thompson
  • 29,276
  • 6
  • 41
  • 48