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?
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.
Related
I have a database that contains images, and I want to serve these images through some urls, preferably like so:
foobar.com/items?id=whateverTheImageIdIsInTheDatabase.
So I wrote this code:
(defn create-item-image []
(let [item-id (:id (:params req))
item
(find-by-id
"items"
(ObjectId. item-id)
)
file-location (str "resources/" item-id ".jpg")
]
(with-open [o (io/output-stream file-location)]
(let [
;; take the first image. The "image" function simply returns the data-url from the id of the image stored in (first (:images item))
img-string (get (str/split (image (first (:images item))) #",") 1)
img-bytes
(.decode (java.util.Base64/getDecoder) img-string)
]
;; write to a file with the name whateverTheImageIdIsInTheDatabase.jpg
(.write o img-bytes)
(.close o)
)
)
)
)
(defn image-handler [req]
(do
(prn "coming to image handler")
(create-item-image req)
;; send the resourc whateverTheImageIdIsInTheDatabase.jpg created above.
(assoc (resource-response (str (:_id (:params req)) ".jpg") {:root ""})
:headers {"Content-Type" "image/jpeg; charset=UTF-8"})
)
)
But this doesn't work. The resources are broken. Is it because the resource is sent before the file is created? How do I do send the resource on the fly? Another issue with writing a file on disk is that it has to stay there. So if 1000 requests for different images are made, all 1000 files would be stored in the server, which shouldn't be necessary since they are already in the database. Ultimately, how can I send these images stored as data-urls as files in the response without having to first write them to the disk?
Resources are static files which do not change over running the application and they are packed into uberjar when the server is compiled for production.
If you want to server an image in response, just convert it into byte array and send it within :body of response.
I would like to create programatically a sub page for a known parent. How can I do that? The page creation will takes place in a signal receiver: the page is created on publication of another page.
Add a revision as well.
from wagtail.wagtailcore.models import Page
from models import MyPage
home = Page.objects.get(id=3) # or better Page query
my_page = MyPage(title="test", body="<h1>the body</h1>")
home.add_child(instance=my_page)
# later when a cms user updates the page manually
# there will be no first revision to compare against unless
# you add a page revision also programmatically.
my_page.save_revision().publish()
You can see how wagtail does this in the wagtailadmin pages create view (line 156).
https://github.com/wagtail/wagtail/blob/stable/1.13.x/wagtail/wagtailadmin/views/pages.py
Update 2018-09-18:
I built a 700 page site including 200 generated pages. I never added an initial Revision anywhere and no editors complained. After the first manual edit there will be a Revision. Go ahead and add an initial Revision if you think it is needed for traceability.
To create a page programmatically:
page = SomePageType(title="My new page", body="<p>Hello world</p>") # adjust fields to match your page type
parent_page.add_child(instance=page)
Below is my complete code to create a multi language page structure programatically. It will replace the "Wagtail Welcome Page" with a LanguageRedirectionPage instance.
More information about multi language pages:
Wagtail Docs - Internationalization
The page structure is as follows:
Page
LanguageRedirectionPage (will redirect to /en)
Page (en)
Page (de)
Page (fr)
where the created Site instance at the end of the code points to the LanguageRedirectionPage instance. This is the entry point of our application.
# Deletes existing pages and sites
Site.objects.all().delete()
Page.objects.filter(pk=2).delete() # Deletes Wagtail welcome page
root_page = Page.objects.filter(pk=1).get()
# Adds a LanguageRedirectionPage as a child of the Root Page
app_name = '[Your Project Name]'
page_slug = app_name.lower().replace(" ", "")
sub_root_page = LanguageRedirectionPage(
title=app_name,
draft_title=app_name,
slug=page_slug,
live=True,
owner=account,
)
root_page.add_child(instance=sub_root_page)
sub_root_page.save_revision().publish()
# Adds some language pages
for code,caption in dict(settings.LANGUAGES).items():
print(code, caption)
sub_root_page.add_child(instance=Page(
title=caption,
slug=code,
live=True,
owner=account,
))
# Adds a new Site instance (See Settings -> Sites in your Wagtail admin panel)
Site.objects.create(
hostname='localhost',
port='80',
site_name=app_name,
root_page=sub_root_page,
is_default_site=True,
)
Is it possible to use template parameters in the content of a post with Hugo? E.g. if I have the following parameters:
[params.company]
name = "My Company"
Can I then do something like this in the content of a post?
This site, {{ .Site.BaseURL }} is operated by {{ params.company.name }}
I've tried, but it's literally printing the above instead of interpolating the variables.
1. Front matter way
As far as I'm aware, it's not possible* to put variables within the markdown file's content because MD parser would strip them, but it's possible to do it using custom variables on the front matter of each .md content file. The Hugo engine can target any fields you set in front matter. Front matter fields can be unique as well.
In your case, the template which is called to show the rendered .MD file has access to front matter parameters and you can change template's behaviour (like add classes of extra <div>'s) or even pull the content right from the parameter.
For example, on my .md files I have:
---
title: "Post about the front matter"
<more key pairs>
nointro: false
---
The key nointro: true would make my first paragraph to be normal size. Otherwise, if key is absent or false, first paragraph would be shown at larger font size. Technically, it's adding a custom class on a <div>.
In the template, I tap into the custom parameter nointro this way:
parent template which shows your markdown file, which has front matter parameters:
<div class="article-body {{ if .Params.nointro }} no_intro {{ end }}">
{{ .Content }}
</div><!-- .article-body -->
Notice I can't put variables within {{ .Content }}, but I can outside of it.
For posterity, that's piece of the content from a file hugo/themes/highlighter/layouts/partials/blog-single-content.html, it's a partial for single post content. You can arrange your partials any way you like.
Obviously, that's Boolean parameter flag, but equally it could be content which you could use directly:
MD file's top:
---
title: "One of our clients"
<more key pairs>
companyname: "Code and Send Ltd"
---
Text content is put here.
Then, reference it like this (extra insurance against blank value using IF):
Anywhere in Hugo template files:
{{ if .Params.companyname }}{{ .Params.companyname }}{{ end }}
2. Using config.(toml/yaml/json)
Now, looking at your example, "This site is operated by " would almost warrant a custom field in more global location, for example, hugo/config.toml. If I wanted to add a companyname into my config, I'd do it this way:
hugo/config.toml:
BaseURL = "_%%WWWPATH%%_"
languageCode = "en-uk"
title = "Code and Send"
pygmentsUseClasses = true
author = "Roy Reveltas"
theme = "Highlighter"
[params]
companyname = ""
Then I'd use it anywhere via {{ .Site.Params.headercommentblock }}.
I guess if you want your client pages to be static pages then single location might not be the best and you might want to tap into front-matter. Otherwise, if it's a site's footer, this way is better. Alternatively, you could even put this data even on data files.
3. Using custom placeholders and replacing via Gulp/npm scripts
I said not possible*, but it's possible, although unconventional and more risky.
I had such setup when I needed two builds for my website: 1) Prod and 2) Dev. Prod URL's were coming from two places: CDN and my server. Dev had to come from a single place in a static folder because I wanted to see images and was often working offline on a train.
To solve that, I used two custom variables in all my templates (including markdown content): _%%WWWPATH%%_ and _%%CDNPATH%%_. I came up with this unique pattern myself by the way, feel free to adapt it. Then, I put it also on hugo/config.toml as:
hugo/config.toml:
BaseURL = "_%%WWWPATH%%_"
After Hugo happily generated the website with those placeholders, I finished off the HTML's using a Grunt task:
grunt file:
replace: {
dev: {
options: {
patterns: [{
match: /_%%CDNPATH%%_+/g,
replacement: function () {
return 'http://127.0.0.1:1313/'
}
}, {
match: /_%%WWWPATH%%_+/g,
replacement: function () {
return 'http://127.0.0.1:1313/'
}
}...
For posterity, I recommend Gulp and/or npm scripts, I'd avoid Grunt. This is my older code example above from the days when Grunt was the best.
If you go this route, it's riskier than Hugo params because Hugo won't error-out when your placeholder values are missing or anything else wrong happens and placeholders might spill into the production code.
Going this route you should add multiple layers of catch-nets, ranging from simple following Gulp/Grunt/npm scripts step which searches for your placeholder pattern to pre-commit hooks ran via Husky on npm scripts that prevent from committing any code that has certain patterns (for example, %%_). For example, at a very basic level, you would instruct Husky to search for anything before allowing committing this way:
package.json of your repo:
"scripts": {
"no-spilled-placeholders": "echo \"\n\\033[0;93m* Checking for spilled placeholders:\\033[0m\"; grep -q -r \"%%_\" dist/ && echo \"Placeholders found! Stopping.\n\" && exit 1 || echo \"All OK.\n\"",
"precommit": "npm run no-spilled-placeholders"
},
Basically, grep for pattern %%_ and exit with error code if found. Don't forget to escape the code because it's JSON. I use similar (more advanced) setup in production and nothing slips through. In proper setup you should creatively look for anything mis-typed, including: %_, _%, %__, __% and so on.
Normal Go template is not supported in the markdown file, but shortcodes are:
{{< param "company.name" >}}
To access arbitrary other Go template values, create a custom shortcode for it and call that from your markdown file.
For your example, you need the site's baseUrl, so save this as layouts/shortcodes/base_url.html:
{{ .Site.BaseURL }}
And write this in your markdown file:
+++
[company]
name = "My Company"
+++
This site, {{< base_url >}} is operated by {{< param "company.name" >}}
There is also the shortcode param : {{< param "companyName" >}} : https://gohugo.io/content-management/shortcodes/#param
As a React/Om newbie I am not sure if this issue is Om-specific.
I want to build a date entry component based on free text entry. It includes an <input> field where they can type, and a <p> displaying the parsed date (if it's valid).
I implemented it as:
(defn parse-date [e owner]
(let [text (.. e -target -value)]
(om/set-state! owner :parsed-date text)))
(defn date-entry [app owner]
(reify
om/IInitState
(init-state [_] {:parsed-date ""})
om/IRenderState
(render-state [this state]
(dom/div nil
(dom/input #js {:type "text"
:ref "date"
:id "new-date"
:onChange #(parse-date % owner)})
(dom/p nil (:parsed-date state))))))
Unfortunately, as soon as I plug this change handler in, it doesn't behave as expected. When I type a digit in the input field, I can see it appear in the input and the <p> next to it, but then it disappears from the input immediately.
I am able to work it around by putting the text on state:
(defn parse-date [e owner]
(let [text (.. e -target -value)]
(om/set-state! owner :parsed-date text)
(om/set-state! owner :text text)))
(defn date-entry [app owner]
(reify
om/IInitState
(init-state [_] {:parsed-date "" :text ""})
om/IRenderState
(render-state [this state]
(dom/div nil
(dom/input #js {:type "text"
:ref "date"
:id "new-date"
:onChange #(parse-date % owner)
:value (:text state)})
(dom/p nil (:parsed-date state))))))
However, I am surprised I had to do it. Is it really necessary? Can someone please explain what's going on here or point me to relevant docs? Why does plugging in a change handler calling set-state! swallow the event?
Yes it's necessary. Every time the state changes, DOM re-renders and clears your input's value. So if you have no :value in your :input's attributes, it will be cleared.
The reason for this is that when React.js starts diffing the real DOM, with the virtual one, it finds that there's some value in the real attribute, while there is none in the virtual DOM, and therefore it clears it, assuming that's what you want. You should always be explicit about what the DOM should look like (eg. your 2nd snippet).
My app, Datastore, webapp2, and form-specific "responses" are all working :) but I need the page to load without displaying previous visitor's query results. I need query results only for current form-submitter, after they submit form. Is this a session or headers solution, or can I edit the GqlQuery to accomplish this?
messages = db.GqlQuery("SELECT * "
"FROM Visitor "
"ORDER BY date DESC LIMIT 1") #obviously shows previous form submit
for message in messages:
if message.name == "" or message.mood == "":
self.response.out.write("<div class='textright'>Type name and select.</div>")
self.response.out.write("</body></html>")
elif message.mood == "bad" and message.name != "":
self.response.out.write("<body><html>")
self.response.out.write("<div class='textright'>Stay the course
^ ^ this last section is my "response" that needs to appear only after current visitor submits form.
I would strongly recommend you to go through the Getting Started and especially the templates section, until you will understand how it works.
But you if you just want to see your example in action try this (read more):
class Process(webapp.RequestHandler):
def post(self):
name = self.request.get("name")
mood = self.request.get("mood")
if mood == "bad" and name != "":
self.response.out.write("<html><body>")
self.response.out.write("<h1>Welcome to the Internet!</h1>")
self.response.out.write("<p>My mood is %s and my name is %s</p>" % (mood, name))
self.response.out.write("</body></html>")
else:
self.response.out.write("<html><body>")
self.response.out.write("<h1>Welcome to the Internet anyway!</h1>")
self.response.out.write("</body></html>")
Also never use print in your GAE applications, use the logger instead for debugging and more.
If you want to emit values for debugging purposes, particularly if you want that before an <html> tag is written, try
self.response.out.write("<!-- name: %s -->" % self.request.get("name"))
Otherwise, the browser might get confused.
print from a handler will never to what you expect.
In your snippet, you haven't shown where var7 and var9 come from.
I do realize that post/.put form values to Datastore automatically redirects user to new page
I think you misunderstand. You haven't shown us where your code does a put() or a redirect. A post() handler does not automatically do either.
Which tutorial are you looking at? Perhaps we need to tighten up vague wording.