Automate Moving PRFiles to Web Server

Take a picture with a camera

In Pier pictures can be added to a page by creating a PRFile, or by using a webserver and the IMG html tag. In the past I have avoided PRFiles because:

  • The IMG can include ALT text (alternative text), PRFiles do not have a field for this
  • using a separate webserver reduces the load on the machine serving Pier (in terms of both disk space and CPU)

Previously there were discussions on the Seaside/Pier newsgroup about saving pictures in PRFiles causing image bloat. In several tests I've ran, this didn't seem to be a problem - additional space taken by edits or space cleared by garbage collection well outpaced space changes from adding PRFiles. It may be that a test running over several weeks would indicate an issue.

Unfortunately using S3 or a separate image server requires a fair amount of technical skill for the wiki users. While I was on vacation I only had access to a phone and lacked the keys to add pictures to my usual S3 bucket, so using PRFiles were useful in this situation.

Why - what is the error if upload is used? Saving files in Pier does not appear to impact the image size significantly, it also puts more stress on the Pier system. Unfortunately the images served by Pier do not have alt-text, so using an external web server (like Amazon S3) allows this through HTML. Pictures also take up more space than text.

How:

  1. Create an embedded link for the picture like this and save the page:

    {{{html: <div style="clear: both">}}} *Picture_Name.png|embedded=true* {{{html: </div>}}}

    The HTML causes text below the picture to be on a separate line, otherwise it looks like:

    Sample text before an image, followed by an empty line.

    Sample text after the picture, with an empty line above and below. Note that the text is wrapped around the image, including:

    • lists
    • but not block-elements like lines
    • for the picture at the top of this page, if the div surrounding the img is removed the it is no longer right aligned

  2. In the page click the Picture_Name.png link and choose PRFile for the type
  3. Copy the name, save without uploading anything
  4. Search for the file name, click on the link that has the file name
  5. Choose edit
  6. Browse and find the file. Save without clicking upload
  7. The picture should be on the page

Questions:

  • Can I get rid of the upload button? It is from MAFileUploadComponent>>#renderUploadOn:
  • Why is the File: Input is conflicting with concurrent modification error popping up for the Toshiba laptop, but not for the one on the VC. Is there code cached somewhere? Could be Input is Conflicting with Concurrent Modification issues.

usingNaming: aNameBlock toExternalUsing: writePictureFilesAndReturnUrls reportOn: aPathString
	"The report may have links to the PRFiles, so consider its location (having aStartPage as a parent prevents the file from being overwritten). User is responsible to select a aStartPageName that does not have private pictures under it"

	| reportPage files |
	self guessTodoFromPath: aPathString.
	(reportPage := PRPathLookup start: self kernel root path: aPathString)
		ifNotNil: [
			reportPage contents: (String streamContents: [ :outputStream |
					 | batchesOfFiles batchesOfFilesToLinks linksToFile pictureUrls errorLinks |
					 batchesOfFilesToLinks := Dictionary new.
					 files := (PRPathLookup
						           start: self kernel root
						           path: aStartPageName) enumerator everything select: [
						          :e | e isFile and: [ e isImage ] ].
					 files isEmpty
						 ifTrue: [
						 self printReportHeaderOn: outputStream fields: 'No files found' ]
						 ifFalse: [
							 self
								 printReportHeaderOn: outputStream
								 fields: '|!File|!Comment'.
							 files do: [ :file |
								 ((file title reject: [ :c | '-_.:' includes: c ])
									  isAllAlphaNumerics and: [ file title includes: $. ])
									 ifFalse: [
										 outputStream
											 nextPutAll: '|*';
											 nextPutAll: file absolutePath;
											 nextPutAll:
												 '*|needs a title to be used for the file name, edit it to change the title.';
											 cr ]
									 ifTrue: [
										 file hasChildren
											 ifTrue: [
												 outputStream
													 nextPutAll: '|*';
													 nextPutAll: file absolutePath;
													 nextPutAll:
														 '*|Has children, these should be removed first.';
													 cr ]
											 ifFalse: [
												 file isRoot
													 ifTrue: [
														 outputStream
															 nextPutAll: '|*';
															 nextPutAll: file absolutePath;
															 nextPutAll:
																 '*|Should not be the root, removing this would be very bad.';
															 cr ]
													 ifFalse: [
														 ([
														  [
														  (aNameBlock value: file) asUrl retrieveContents
															  isEmpty ]
															  on: ZnHttpUnsuccessful
															  do: [ true ] ]
															  on: ConnectionTimedOut
															  do: [ true ])
															 ifFalse: [
																 outputStream
																	 nextPutAll: '|*';
																	 nextPutAll: file absolutePath;
																	 nextPutAll: '*|Should be replaced with: ';
																	 nextPutAll: (aNameBlock value: file) asString;
																	 cr ]
															 ifTrue: [
																 errorLinks := (linksToFile := self kernel root
																	                               incomingReferences
																                               to: file) reject: [
																	               :link | link isEmbedded ].
																 linksToFile size = 0
																	 ifTrue: [
																		 outputStream
																			 nextPutAll: '|*';
																			 nextPutAll: file absolutePath;
																			 nextPutAll:
																				 '*|Has no links to it, need to wait until there is an embedded link to this.';
																			 cr ]
																	 ifFalse: [
																		 errorLinks isNotEmpty
																			 ifTrue: [
																				 outputStream
																					 nextPutAll: '|*';
																					 nextPutAll: file absolutePath;
																					 nextPutAll: '*|Has a non-embedded link on *';
																					 nextPutAll: errorLinks anyOne owner absolutePath;
																					 nextPutAll: '* - this should be embedded.';
																					 cr;
																					 cr ]
																			 ifFalse: [
																			 batchesOfFilesToLinks at: file put: linksToFile ] ] ] ] ] ] ].
							 writePictureFilesAndReturnUrls value:
								 (batchesOfFiles := batchesOfFilesToLinks keys).
							 pictureUrls := batchesOfFiles collect: aNameBlock.
							 batchesOfFiles with: pictureUrls do: [ :prFile :urlString |
								 urlString isEmpty
									 ifTrue: [
										 outputStream
											 nextPutAll: '|*';
											 nextPutAll: prFile absolutePath;
											 nextPutAll:
												 '*|The URL is empty, unable to create the picture file.';
											 cr ]
									 ifFalse: [
										 ([ urlString asUrl retrieveContents isEmpty ]
											  on: ZnHttpUnsuccessful
											  do: [ true ])
											 ifTrue: [
												 outputStream
													 nextPutAll: '|*';
													 nextPutAll: prFile absolutePath;
													 nextPutAll: '*|The URL is *';
													 nextPutAll: urlString;
													 nextPutAll:
														 '* - but has an error retrieving the contents.';
													 cr ]
											 ifFalse: [
												 (batchesOfFilesToLinks at: prFile) do: [ :link |
													 outputStream
														 nextPutAll: '|*';
														 nextPutAll: link owner absolutePath;
														 nextPutAll: '*|Update to have *';
														 nextPutAll: urlString asString;
														 nextPutAll: '* for ==';
														 nextPutAll: prFile title;
														 nextPutAll: '==.';
														 cr ] ] ] ] ] ]) ]
Posted by John Borden with tags Pier link