Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F8922047
multiple-files.ts
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
5 KB
Referenced Files
None
Subscribers
None
multiple-files.ts
View Options
import
Router
from
"@koa/router"
;
import
{
Collection
,
File
as
SealiousFile
,
Field
as
SealiousField
,
}
from
"sealious"
;
import
{
Context
}
from
"koa"
;
import
{
inputWrapper
}
from
"../../utils/input-wrapper"
;
import
{
FlatTemplatable
,
tempstream
}
from
"tempstream"
;
import
{
CollectionItem
}
from
"../../../../sealious/@types/src/main"
;
import
{
MultipleFiles
as
MultipleFilesField
}
from
"../fields/multiple-files"
;
import
{
FormDataValue
}
from
"../form"
;
import
{
FormFieldControl
}
from
"./form-field-control"
;
import
{
is
,
predicates
}
from
"@sealcode/ts-predicates"
;
export
type
MultipleFilesOptions
=
{
label
?:
string
;
uploadLabel
?:
string
};
export
class
MultipleFiles
extends
FormFieldControl
{
public
options
:
MultipleFilesOptions
;
constructor
(
public
field
:
MultipleFilesField
,
options
?:
MultipleFilesOptions
)
{
super
([
field
]);
this
.
options
=
options
||
{};
}
mount
(
router
:
Router
)
{
router
.
get
(
this
.
getFrameRelativeURL
(),
async
(
ctx
)
=>
{
ctx
.
body
=
this
.
renderFrame
(
ctx
,
await
this
.
renderFrameContent
(
ctx
)
);
});
router
.
post
(
this
.
getFrameRelativeURL
()
+
"/delete/:file_item_id"
,
async
(
ctx
)
=>
{
await
this
.
deleteFileAssociation
(
ctx
,
ctx
.
params
.
file_item_id
);
ctx
.
body
=
this
.
renderFrame
(
ctx
,
await
this
.
renderFrameContent
(
ctx
)
);
}
);
router
.
post
(
this
.
getFrameRelativeURL
()
+
"/add"
,
async
(
ctx
)
=>
{
const
file
=
ctx
.
$body
.
file
;
if
(
!
file
||
!
is
(
file
,
predicates
.
object
))
{
ctx
.
body
=
"Missing file"
;
return
;
}
await
this
.
addFileAssociation
(
ctx
,
file
as
unknown
as
SealiousFile
);
ctx
.
body
=
this
.
renderFrame
(
ctx
,
await
this
.
renderFrameContent
(
ctx
)
);
});
}
async
deleteFileAssociation
(
ctx
:
Context
,
file_item_id
:
string
)
{
await
ctx
.
$app
.
collections
[
this
.
field
.
collection_field
.
referencing_collection
].
removeByID
(
ctx
.
$context
,
file_item_id
);
}
async
addFileAssociation
(
ctx
:
Context
,
file
:
SealiousFile
)
{
const
file_field
=
this
.
getFileField
();
if
(
!
file_field
)
{
throw
new
Error
(
"No file field in referencing collection"
);
}
const
body
=
{
[
this
.
field
.
collection_field
.
referencing_field
]
:
await
this
.
field
.
getItemId
(
ctx
),
[
file_field
.
name
]
:
file
,
};
await
ctx
.
$app
.
collections
[
this
.
field
.
collection_field
.
referencing_collection
].
create
(
ctx
.
$context
,
body
);
}
getFrameRelativeURL
()
{
return
`
${
this
.
field
.
name
}
_files`
;
}
getFrameID
()
:
string
{
// the "A" is necessary here
return
`A
${
this
.
field
.
name
}
__multiple-fields-control`
;
}
renderFrame
(
ctx
:
Context
,
content
?:
FlatTemplatable
)
{
return
tempstream
/* HTML */
`<turbo-frame
${
content
?
""
:
`src="./
${
this
.
getFrameRelativeURL
()
}
"`
}
id="
${
this
.
getFrameID
()
}
"
target="_top"
>
${
content
||
""
}
</turbo-frame>`
;
}
render
(
ctx
:
Context
,
_data
:
Record
<
string
,
FormDataValue
>
,
_messages
:
[],
field_name_prefix
:
string
,
form_id
:
string
,
validate
:
boolean
)
:
FlatTemplatable
|
Promise
<
FlatTemplatable
>
{
return
this
.
renderFrame
(
ctx
,
""
);
}
getReferencingCollection
()
{
const
result
=
this
.
field
.
collection_field
.
app
.
collections
[
this
.
field
.
collection_field
.
referencing_collection
];
return
result
;
}
getFileField
()
:
SealiousField
|
null
{
for
(
const
[
field_name
,
field
]
of
Object
.
entries
(
this
.
getReferencingCollection
().
fields
))
{
if
(
field
.
handles_large_data
)
{
return
field
;
}
}
return
null
;
}
async
extractFileFromItem
(
item
:
CollectionItem
)
:
Promise
<
SealiousFile
|
null
>
{
const
sealious_field
=
this
.
getFileField
();
if
(
!
sealious_field
)
{
return
null
;
}
return
await
SealiousFile
.
fromID
(
(
item
.
collection
as
Collection
).
app
,
item
.
get
(
sealious_field
.
name
).
id
);
}
async
renderFileItem
(
fileItem
:
CollectionItem
,
file
:
SealiousFile
)
:
Promise
<
FlatTemplatable
>
{
return
/* HTML */
`<li>
<a href="
${
file
.
getURL
()
}
" data-turbo="false">
${
file
.
filename
||
"no filename"
}
</a>
<form
data-turbo-frame="
${
this
.
getFrameID
()
}
"
action="
${
this
.
getFrameRelativeURL
()
}
/delete/
${
fileItem
.
id
}
"
method="POST"
>
<input type="submit" value="X" class="file-list-action" />
</form>
</li>`
;
}
async
getFileItems
(
ctx
:
Context
)
{
const
item_id
=
await
this
.
field
.
getItemId
(
ctx
);
const
{
items
:
[
item
],
}
=
await
this
.
field
.
collection_field
.
collection
.
list
(
ctx
.
$context
)
.
ids
([
item_id
])
.
attach
({
[
this
.
field
.
collection_field
.
name
]
:
true
})
.
fetch
();
return
item
.
getAttachments
(
this
.
field
.
collection_field
.
name
)
.
filter
((
f
)
=>
f
);
}
async
renderFrameContent
(
ctx
:
Context
)
:
Promise
<
FlatTemplatable
>
{
const
files
=
(
await
Promise
.
all
(
(
await
this
.
getFileItems
(
ctx
)
).
map
(
async
(
item
)
=>
[
item
,
await
this
.
extractFileFromItem
(
item
),
])
)
).
filter
(([
_
,
f
])
=>
f
!==
null
)
as
[
CollectionItem
,
SealiousFile
][];
return
inputWrapper
(
[
"multiple-files"
,
this
.
field
.
name
],
tempstream
/* HTML */
`
<label>
${
this
.
options
.
label
||
this
.
field
.
name
}
</label>
<ul>
${
files
.
map
(([
item
,
file
])
=>
this
.
renderFileItem
(
item
,
file
)
)
}
</ul>
<form
action="
${
this
.
getFrameRelativeURL
()
}
/add"
method="POST"
enctype="multipart/form-data"
data-turbo-frame="
${
this
.
getFrameID
()
}
"
>
<input type="file" name="file" />
<input
type="submit"
value="
${
this
.
options
.
uploadLabel
||
"Upload"
}
"
class="file-list-action"
/>
</form>
`
);
}
}
File Metadata
Details
Attached
Mime Type
text/html
Expires
Sat, Sep 20, 14:12 (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
949258
Default Alt Text
multiple-files.ts (5 KB)
Attached To
Mode
rSGEN sealgen
Attached
Detach File
Event Timeline
Log In to Comment