new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
/ *
Rename import paths from camlistore.org to perkeep.org.
Part of the project renaming, issue #981.
After this, users will need to mv their $GOPATH/src/camlistore.org to
$GOPATH/src/perkeep.org. Sorry.
This doesn't yet rename the tools like camlistored, camput, camget,
camtool, etc.
Also, this only moves the lru package to internal. More will move to
internal later.
Also, this doesn't yet remove the "/pkg/" directory. That'll likely
happen later.
This updates some docs, but not all.
devcam test now passes again, even with Go 1.10 (which requires vet
checks are clean too). So a bunch of vet tests are fixed in this CL
too, and a bunch of other broken tests are now fixed (introduced from
the past week of merging the CL backlog).
Change-Id: If580db1691b5b99f8ed6195070789b1f44877dd4
2018-01-01 22:41:41 +00:00
Copyright 2017 The Perkeep Authors .
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package main
import (
"fmt"
"html/template"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"regexp"
"strconv"
"strings"
"time"
Rename import paths from camlistore.org to perkeep.org.
Part of the project renaming, issue #981.
After this, users will need to mv their $GOPATH/src/camlistore.org to
$GOPATH/src/perkeep.org. Sorry.
This doesn't yet rename the tools like camlistored, camput, camget,
camtool, etc.
Also, this only moves the lru package to internal. More will move to
internal later.
Also, this doesn't yet remove the "/pkg/" directory. That'll likely
happen later.
This updates some docs, but not all.
devcam test now passes again, even with Go 1.10 (which requires vet
checks are clean too). So a bunch of vet tests are fixed in this CL
too, and a bunch of other broken tests are now fixed (introduced from
the past week of merging the CL backlog).
Change-Id: If580db1691b5b99f8ed6195070789b1f44877dd4
2018-01-01 22:41:41 +00:00
uistatic "perkeep.org/app/scanningcabinet/ui"
2018-01-03 05:03:30 +00:00
"perkeep.org/internal/httputil"
"perkeep.org/internal/magic"
Rename import paths from camlistore.org to perkeep.org.
Part of the project renaming, issue #981.
After this, users will need to mv their $GOPATH/src/camlistore.org to
$GOPATH/src/perkeep.org. Sorry.
This doesn't yet rename the tools like camlistored, camput, camget,
camtool, etc.
Also, this only moves the lru package to internal. More will move to
internal later.
Also, this doesn't yet remove the "/pkg/" directory. That'll likely
happen later.
This updates some docs, but not all.
devcam test now passes again, even with Go 1.10 (which requires vet
checks are clean too). So a bunch of vet tests are fixed in this CL
too, and a bunch of other broken tests are now fixed (introduced from
the past week of merging the CL backlog).
Change-Id: If580db1691b5b99f8ed6195070789b1f44877dd4
2018-01-01 22:41:41 +00:00
"perkeep.org/pkg/app"
"perkeep.org/pkg/auth"
"perkeep.org/pkg/blob"
"perkeep.org/pkg/client"
"perkeep.org/pkg/constants"
"perkeep.org/pkg/fileembed"
"perkeep.org/pkg/search"
camliserver "perkeep.org/pkg/server"
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
"go4.org/syncutil"
)
const (
maxScan = 50 // number of scans fetched/displayed. arbitrary.
maxDue = 30 // number of due documents fetched
scanNodeType = "scanningcabinet:scan"
documentNodeType = "scanningcabinet:doc"
)
var (
rootTemplate = template . Must ( template . New ( "root" ) . Parse ( rootHTML ) )
docTemplate = template . Must ( template . New ( "doc" ) . Parse ( docHTML ) )
resourcePattern * regexp . Regexp = regexp . MustCompile ( ` ^/resource/( ` + blob . Pattern + ` )$ ` )
)
// config is used to unmarshal the application configuration JSON
// that we get from Camlistore when we request it at $CAMLI_APP_CONFIG_URL.
type extraConfig struct {
2017-03-19 05:27:00 +00:00
Auth string ` json:"auth,omitempty" ` // userpass:username:password
HTTPSCert string ` json:"httpsCert,omitempty" ` // path to the HTTPS certificate file.
HTTPSKey string ` json:"httpsKey,omitempty" ` // path to the HTTPS key file.
SourceRoot string ` json:"sourceRoot,omitempty" ` // Path to the app's resources dir, such as html and css files.
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
}
func appConfig ( ) ( * extraConfig , error ) {
configURL := os . Getenv ( "CAMLI_APP_CONFIG_URL" )
if configURL == "" {
2017-03-23 03:57:52 +00:00
logf ( "CAMLI_APP_CONFIG_URL not defined, the app will run without any auth" )
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
return nil , nil
}
cl , err := app . Client ( )
if err != nil {
return nil , fmt . Errorf ( "could not get a client to fetch extra config: %v" , err )
}
conf := & extraConfig { }
if err := cl . GetJSON ( configURL , conf ) ; err != nil {
return nil , fmt . Errorf ( "could not get app extra config at %v: %v" , configURL , err )
}
return conf , nil
}
type handler struct {
httpsCert string
httpsKey string
am auth . AuthMode
mux * http . ServeMux
sh search . QueryDescriber
// TODO(mpl): later we should have an uploader interface instead. implemented by *client.Client like sh, but they wouldn't have to be the same in theory. right now they actually are.
cl * client . Client
ih * camliserver . ImageHandler
signer blob . Ref
server string
}
func newHandler ( ) ( * handler , error ) {
cl , err := app . Client ( )
if err != nil {
return nil , fmt . Errorf ( "could not initialize a client: %v" , err )
}
h := & handler {
sh : cl ,
cl : cl ,
}
2017-03-19 05:27:00 +00:00
config , err := appConfig ( )
if err != nil {
return nil , err
}
// Serve files from source root when running devcam
if config . SourceRoot != "" {
2017-03-23 03:57:52 +00:00
logf ( "Using UI resources (HTML, JS, CSS) from disk, under %v" , config . SourceRoot )
2017-03-19 05:27:00 +00:00
uistatic . Files = & fileembed . Files {
DirFallback : config . SourceRoot ,
}
}
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
mux := http . NewServeMux ( )
mux . HandleFunc ( "/" , h . handleRoot )
2017-03-19 05:27:00 +00:00
mux . HandleFunc ( "/ui/" , handleUiFile )
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
mux . HandleFunc ( "/uploadurl" , h . handleUploadURL )
mux . HandleFunc ( "/upload" , h . handleUpload )
mux . HandleFunc ( "/resource/" , h . handleResource )
mux . HandleFunc ( "/makedoc" , h . handleMakedoc )
mux . HandleFunc ( "/doc/" , h . handleDoc )
mux . HandleFunc ( "/changedoc" , h . handleChangedoc )
mux . HandleFunc ( "/robots.txt" , handleRobots )
h . mux = mux
if err := h . disco ( ) ; err != nil {
return nil , err
}
if config != nil {
h . httpsCert = config . HTTPSCert
h . httpsKey = config . HTTPSKey
}
var authConfig string
if config == nil || config . Auth == "" {
authConfig = "none"
} else {
authConfig = config . Auth
}
am , err := auth . FromConfig ( authConfig )
if err != nil {
return nil , err
}
h . am = am
return h , nil
}
func ( h * handler ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) {
if auth . AllowedWithAuth ( h . am , r , auth . OpAll ) {
h . serveHTTP ( w , r )
return
}
if us , ok := h . am . ( auth . UnauthorizedSender ) ; ok {
if us . SendUnauthorized ( w , r ) {
return
}
}
w . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=scanning cabinet" )
w . WriteHeader ( http . StatusUnauthorized )
fmt . Fprintf ( w , "<html><body><h1>Unauthorized</h1>" )
}
func ( h * handler ) serveHTTP ( w http . ResponseWriter , r * http . Request ) {
if h . mux == nil {
http . Error ( w , "handler not properly initialized" , http . StatusInternalServerError )
return
}
r . URL . Path = strings . Replace ( r . URL . Path , app . PathPrefix ( r ) , "/" , 1 )
h . mux . ServeHTTP ( w , r )
}
type rootData struct {
BaseURL string
2017-02-12 08:02:57 +00:00
Tags separatedString
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
Media [ ] MediaObjectVM
SearchedDocs [ ] DocumentVM
UntaggedDocs [ ] DocumentVM
UpcomingDocs [ ] DocumentVM
TopMessage template . HTML
ErrorMessage string
2017-02-09 06:21:24 +00:00
AllTags map [ string ] int
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
}
func ( h * handler ) disco ( ) error {
var err error
server := os . Getenv ( "CAMLI_API_HOST" )
if server == "" {
server , err = h . cl . BlobRoot ( )
if err != nil {
return fmt . Errorf ( "CAMLI_API_HOST var not set, and client could not discover server blob root: %v" , err )
}
}
h . server = server
// TODO(mpl): setup our own signer if we got our own key and stuff.
signer , err := h . cl . ServerPublicKeyBlobRef ( )
if err != nil {
return fmt . Errorf ( "client has no signing capability and server can't sign for us either: %v" , err )
}
h . signer = signer
return nil
}
func ( h * handler ) handleRoot ( w http . ResponseWriter , r * http . Request ) {
topMessage := ""
if saved_doc := r . FormValue ( "saved_doc" ) ; saved_doc != "" {
topMessage = fmt . Sprintf ( "Saved <a href='doc/%s'>doc %s</a>" , saved_doc , saved_doc )
}
errorMessage := r . FormValue ( "error_message" )
limit := maxScan
if limitparam := r . FormValue ( "limit" ) ; limitparam != "" {
newlimit , err := strconv . Atoi ( limitparam )
if err == nil {
limit = newlimit
}
}
var (
movm [ ] MediaObjectVM
searchedDocs [ ] DocumentVM
)
tags := newSeparatedString ( r . FormValue ( "tags" ) )
docs , err := h . fetchDocuments ( limit , searchOpts { tags : tags } )
if err != nil {
httputil . ServeError ( w , r , err )
return
}
if len ( tags ) != 0 {
searchedDocs = MakeDocumentViewModels ( docs )
// We've just done a search, in which case we don't show the scans,
2017-02-09 06:21:24 +00:00
// so no need to look for them.
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
} else {
// fetch media objects
mediaObjects , err := h . fetchScans ( limit )
if err != nil {
httputil . ServeError ( w , r , err )
return
}
movm = MakeMediaObjectViewModels ( mediaObjects )
2017-02-09 06:21:24 +00:00
}
allTags , err := h . fetchTags ( )
if err != nil {
httputil . ServeError ( w , r , err )
return
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
}
// fetch upcoming documents
upcoming , err := h . fetchDocuments ( maxDue , searchOpts { due : true } )
if err != nil {
httputil . ServeError ( w , r , err )
return
}
// fetch untagged documents
untagged , err := h . fetchDocuments ( limit , searchOpts { untagged : true } )
if err != nil {
httputil . ServeError ( w , r , err )
return
}
d := rootData {
BaseURL : baseURL ( r ) ,
2017-02-12 08:02:57 +00:00
Tags : tags ,
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
Media : movm ,
SearchedDocs : searchedDocs ,
UntaggedDocs : MakeDocumentViewModels ( untagged ) ,
UpcomingDocs : MakeDocumentViewModels ( upcoming ) ,
TopMessage : template . HTML ( topMessage ) ,
ErrorMessage : errorMessage ,
AllTags : allTags ,
}
if err := rootTemplate . Execute ( w , d ) ; err != nil {
2017-03-23 03:57:52 +00:00
logf ( "root template error: %v" , err )
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
httputil . ServeError ( w , r , err )
return
}
}
func baseURL ( r * http . Request ) string {
scheme := "http"
if r . TLS != nil {
scheme = "https"
}
return fmt . Sprintf ( "%s://%s%s" , scheme , r . Host , app . PathPrefix ( r ) )
}
func ( h * handler ) handleUploadURL ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Type" , "text/plain" )
fmt . Fprintf ( w , "%supload" , baseURL ( r ) )
return
}
func ( h * handler ) handleUpload ( w http . ResponseWriter , r * http . Request ) {
if r . Method != "POST" {
http . Error ( w , "not a POST" , http . StatusMethodNotAllowed )
return
}
mr , err := r . MultipartReader ( )
if err != nil {
httputil . ServeError ( w , r , err )
return
}
var br blob . Ref
var fileName string
var creation time . Time
cr := countingReader { }
for {
part , err := mr . NextPart ( )
if err == io . EOF {
break
}
if err != nil {
httputil . ServeError ( w , r , err )
return
}
name := part . FileName ( )
if name == "" {
// Let the user provide a creation time (for migrating Brad's data)
if part . FormName ( ) != "creation" {
2017-03-23 03:57:52 +00:00
logf ( "form field %q provided in upload, but ignored" , part . FormName ( ) )
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
continue
}
creationParam , err := ioutil . ReadAll ( part )
if err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not read provided creation time: %v" , err ) )
return
}
creation , err = time . Parse ( time . RFC3339 , string ( creationParam ) )
if err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not parse provided creation time %q: %v" , creationParam , err ) )
return
}
continue
}
fileName = path . Base ( name )
cr . r = part
br , err = h . cl . UploadFile ( fileName , & cr , nil )
if err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not write %v to blobserver: %v" , fileName , err ) )
return
}
}
creationProvided := true
if creation . IsZero ( ) {
creationProvided = false
creation = time . Now ( )
}
scan , err := h . fetchScanByContent ( br )
if err == nil {
if creationProvided {
newScan := scan
newScan . creation = creation
if err := h . updateScan ( scan . permanode , & newScan , & scan ) ; err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not update scan: %v" , err ) )
return
}
}
w . Write ( [ ] byte ( scan . permanode . String ( ) ) )
return
}
if ! os . IsNotExist ( err ) {
httputil . ServeError ( w , r , fmt . Errorf ( "could not check if scan with %v already exists: %v" , fileName , err ) )
return
}
scanRef , err := h . createScan ( mediaObject {
contentRef : br ,
creation : creation ,
} )
if err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not create scan object for %v: %v" , fileName , err ) )
return
}
w . Write ( [ ] byte ( scanRef . String ( ) ) )
}
type countingReader struct {
hdr [ ] byte
n int
r io . Reader
}
func ( c * countingReader ) Read ( p [ ] byte ) ( int , error ) {
n , err := c . r . Read ( p )
if c . n < 1024 {
c . hdr = append ( c . hdr , p ... )
}
c . n += n
return n , err
}
func ( c * countingReader ) Mime ( ) string {
return magic . MIMEType ( c . hdr )
}
func ( h * handler ) handleResource ( w http . ResponseWriter , r * http . Request ) {
m := resourcePattern . FindStringSubmatch ( r . URL . Path )
if m == nil {
http . Error ( w , "invalid resource URL" , http . StatusBadRequest )
return
}
scanRef , ok := blob . Parse ( m [ 1 ] )
if ! ok {
http . Error ( w , fmt . Sprintf ( "invalid resource blobref: %q" , m [ 1 ] ) , http . StatusBadRequest )
return
}
mediaObject , err := h . fetchScan ( scanRef )
if err != nil {
if err == os . ErrNotExist {
http . Error ( w , fmt . Sprintf ( "%v not found" , scanRef ) , http . StatusNotFound )
return
}
httputil . ServeError ( w , r , fmt . Errorf ( "resource %v not found: %v" , scanRef , err ) )
return
}
// TODO(mpl): cache and thumbmeta
ih := & camliserver . ImageHandler {
Fetcher : h . cl ,
MaxWidth : search . MaxImageSize ,
MaxHeight : search . MaxImageSize ,
Square : false ,
// TODO(mpl): make the image pkg default to the below when ResizeSem is nil
ResizeSem : syncutil . NewSem ( constants . DefaultMaxResizeMem ) ,
}
if resizeParam := r . FormValue ( "resize" ) ; resizeParam != "" {
resized , err := strconv . Atoi ( resizeParam )
if err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "bogus resize param %q: %v" , resizeParam , err ) )
return
}
ih . MaxWidth = resized
ih . MaxHeight = resized
}
ih . ServeHTTP ( w , r , mediaObject . contentRef )
return
}
func ( h * handler ) handleMakedoc ( w http . ResponseWriter , r * http . Request ) {
if r . Method != "POST" {
http . Error ( w , "not a POST" , http . StatusMethodNotAllowed )
return
}
// gather the media_ids from the form into scanKeys
if r . Form == nil {
r . ParseMultipartForm ( 1 )
}
refs := r . Form [ "blobref" ]
var pages [ ] blob . Ref
for _ , ref := range refs {
br , ok := blob . Parse ( ref )
if ! ok {
httputil . ServeError ( w , r , fmt . Errorf ( "invalid page blobRef %q" , ref ) )
return
}
pages = append ( pages , br )
}
var doc * document
var err error
doc , err = h . fetchDocumentByPages ( pages )
if err != nil {
if ! os . IsNotExist ( err ) {
httputil . ServeError ( w , r , fmt . Errorf ( "could not check if document already existed: %v" , err ) )
return
}
newDoc := document {
pages : pages ,
creation : time . Now ( ) ,
}
docRef , err := h . persistDocAndPages ( newDoc )
if err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not create new document: %v" , err ) )
return
}
newDoc . permanode = docRef
doc = & newDoc
}
noUI := r . Form [ "noui" ]
if len ( noUI ) == 1 && noUI [ 0 ] == "1" {
// For when we just want to get the doc's blobRef as a response.
// Initially for migrating Brad's data, but can't hurt anyway.
w . Write ( [ ] byte ( doc . permanode . String ( ) ) )
return
}
http . Redirect ( w , r , fmt . Sprintf ( "%s%s?size=1200" , baseURL ( r ) , doc . displayURL ( ) ) , http . StatusFound )
}
func ( h * handler ) handleDoc ( w http . ResponseWriter , r * http . Request ) {
urlFields := strings . Split ( r . URL . Path , "/" )
if len ( urlFields ) < 3 {
http . Error ( w , "no document blobref" , http . StatusBadRequest )
return
}
docRef , ok := blob . Parse ( urlFields [ 2 ] )
if ! ok {
http . Error ( w , fmt . Sprintf ( "invalid document blobref: %q" , urlFields [ 2 ] ) , http . StatusBadRequest )
return
}
document , err := h . fetchDocument ( docRef )
if err != nil {
http . Redirect ( w , r , fmt . Sprintf ( "%s?error_message=DocRef+%s+not+found" , baseURL ( r ) , docRef ) , http . StatusFound )
return
}
var pages [ ] mediaObject
for _ , v := range document . pages {
// TODO(mpl): group fetch ?
page , err := h . fetchScan ( v )
if err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not fetch page %v for document %v: %v" , v , document . permanode , err ) )
return
}
pages = append ( pages , page )
}
var size int
size = 1200
if sizeParam := r . FormValue ( "size" ) ; sizeParam != "" {
sizeint , err := strconv . Atoi ( sizeParam )
if err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "invalide size param %q: %v" , sizeParam , err ) )
return
}
size = sizeint
}
show_single_list := size > 600
2017-02-09 06:21:24 +00:00
allTags , err := h . fetchTags ( )
if err != nil {
httputil . ServeError ( w , r , err )
return
}
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
d := struct {
BaseURL string
Pages [ ] MediaObjectVM
Doc DocumentVM
ShowSingleList bool
Size int
2017-02-09 06:21:24 +00:00
AllTags map [ string ] int
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
} {
BaseURL : baseURL ( r ) ,
Pages : MakeMediaObjectViewModels ( pages ) ,
Doc : document . MakeViewModel ( ) ,
ShowSingleList : show_single_list ,
Size : size ,
2017-02-09 06:21:24 +00:00
AllTags : allTags ,
new app: scanning cabinet
WARNING: this app is still experimental, and even its data schema might
change. Do not use in production.
This change adds a Camlistore-based port of the scanning cabinet app
originally created by Brad Fitzpatrick:
https://github.com/bradfitz/scanningcabinet
Some of it is inspired from the App Engine Go port of Patrick Borgeest:
https://bitbucket.org/pborgeest/nometicland
The data schema is roughly as follows:
-a scan is a permanode, with the node type: "scanningcabinet:scan".
-a scan's camliContent attribute is set to the actual image file.
-a scan also holds the "dateCreated" attribute, as well as the
"document" attribute, which references the document this scan is a part
of (if any).
-a document is a permanode, with the node type: "scanningcabinet:doc".
-a document page, is modeled by the "camliPath:sha1-xxx" = "pageNumber"
relation, where sha1-xxx is the blobRef of a scan.
-a document can also hold the following attributes: "dateCreated",
"tag", "locationText", "title", "startDate", and "paymentDueDate".
Known caveats, in decreasing order of concern:
-the data schema might still change.
-the scancab tool, to actually create and upload the files from physical
documents, is practically untested (since I do not own a scanner).
-some parts, in particular related to searches, are probably
sub-optimized.
-the usual unavoidable bugs.
Change-Id: If6afc509e13f7c21164a3abd276fec075a3813bb
2016-07-07 15:53:57 +00:00
}
if err := docTemplate . Execute ( w , d ) ; err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not serve doc template: %v" , err ) )
return
}
}
func ( h * handler ) handleChangedoc ( w http . ResponseWriter , r * http . Request ) {
if r . Method != "POST" {
http . Error ( w , "not a POST" , http . StatusMethodNotAllowed )
return
}
docRef , ok := blob . Parse ( r . FormValue ( "docref" ) )
if ! ok {
httputil . ServeError ( w , r , fmt . Errorf ( "invalid document blobRef %q" , r . FormValue ( "docref" ) ) )
return
}
mode := r . FormValue ( "mode" )
if mode == "break" {
if err := h . breakAndDeleteDoc ( docRef ) ; err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not delete document %v: %v" , docRef , err ) )
return
}
fmt . Fprintf ( w , "<html><body>[<< <a href='%s'>Back</a>] Doc %s deleted and images broken out as un-annotated.</body></html>" , baseURL ( r ) , docRef )
return
}
if mode == "delete" {
if err := h . deleteDocAndImages ( docRef ) ; err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not do full delete of %v: %v" , docRef , err ) )
return
}
fmt . Fprintf ( w , "<html><body>[<< <a href='%s'>Back</a>] Doc %s and its images deleted.</body></html>" , baseURL ( r ) , docRef )
return
}
document := & document { }
document . physicalLocation = r . FormValue ( "physical_location" )
document . title = r . FormValue ( "title" )
document . tags = newSeparatedString ( r . FormValue ( "tags" ) )
docDate , err := dateOrZero ( r . FormValue ( "date" ) , dateformatYyyyMmDd )
if err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not assign new date to document: %v" , err ) )
return
}
document . docDate = docDate
duedate , err := dateOrZero ( r . FormValue ( "due_date" ) , dateformatYyyyMmDd )
if err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not assign new due date to document: %v" , err ) )
return
}
document . dueDate = duedate
if err := h . updateDocument ( docRef , document ) ; err != nil {
httputil . ServeError ( w , r , fmt . Errorf ( "could not update document %v: %v" , docRef , err ) )
return
}
http . Redirect ( w , r , fmt . Sprintf ( "%s?saved_doc=%s" , baseURL ( r ) , docRef ) , http . StatusFound )
}
func handleRobots ( w http . ResponseWriter , r * http . Request ) {
fmt . Fprint ( w , "User-agent: *\nDisallow: /\n" )
}
2017-03-19 05:27:00 +00:00
func handleUiFile ( w http . ResponseWriter , r * http . Request ) {
file := strings . TrimPrefix ( r . URL . Path , "/ui" )
root := uistatic . Files
f , err := root . Open ( "/" + file )
if err != nil {
http . NotFound ( w , r )
2017-03-23 03:57:52 +00:00
logf ( "Failed to open file %v from embedded resources: %v" , file , err )
2017-03-19 05:27:00 +00:00
return
}
defer f . Close ( )
var modTime time . Time
if fi , err := f . Stat ( ) ; err == nil {
modTime = fi . ModTime ( )
}
if strings . HasSuffix ( file , ".css" ) {
w . Header ( ) . Set ( "Content-Type" , "text/css" )
} else if strings . HasSuffix ( file , ".js" ) {
w . Header ( ) . Set ( "Content-Type" , "application/javascript" )
}
http . ServeContent ( w , r , file , modTime , f )
}