Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
heptapod
heptapod
Commits
cdc6d375606a
Commit
08706f68
authored
May 08, 2017
by
Douwe Maan
Browse files
Implement auxiliary blob viewers
parent
4b18ba863f27
Changes
31
Hide whitespace changes
Inline
Side-by-side
app/assets/javascripts/blob/viewer/index.js
View file @
cdc6d375
/* global Flash */
export
default
class
BlobViewer
{
constructor
()
{
BlobViewer
.
initAuxiliaryViewer
();
this
.
initMainViewers
();
}
static
initAuxiliaryViewer
()
{
const
auxiliaryViewer
=
document
.
querySelector
(
'
.blob-viewer[data-type="auxiliary"]
'
);
if
(
!
auxiliaryViewer
)
return
;
BlobViewer
.
loadViewer
(
auxiliaryViewer
);
}
initMainViewers
()
{
this
.
$fileHolder
=
$
(
'
.file-holder
'
);
if
(
!
this
.
$fileHolder
.
length
)
return
;
this
.
switcher
=
document
.
querySelector
(
'
.js-blob-viewer-switcher
'
);
this
.
switcherBtns
=
document
.
querySelectorAll
(
'
.js-blob-viewer-switch-btn
'
);
this
.
copySourceBtn
=
document
.
querySelector
(
'
.js-copy-blob-source-btn
'
);
this
.
simpleViewer
=
document
.
querySelector
(
'
.blob-viewer[data-type="simple"]
'
);
this
.
richViewer
=
document
.
querySelector
(
'
.blob-viewer[data-type="rich"]
'
);
this
.
$fileHolder
=
$
(
'
.file-holder
'
);
const
initialViewer
=
document
.
querySelector
(
'
.blob-viewer:not(.hidden)
'
);
if
(
!
initialViewer
)
return
;
let
initialViewerName
=
initialViewer
.
getAttribute
(
'
data-type
'
);
this
.
simpleViewer
=
this
.
$fileHolder
[
0
].
querySelector
(
'
.blob-viewer[data-type="simple"]
'
);
this
.
richViewer
=
this
.
$fileHolder
[
0
].
querySelector
(
'
.blob-viewer[data-type="rich"]
'
);
this
.
initBindings
();
this
.
switchToInitialViewer
();
}
switchToInitialViewer
()
{
const
initialViewer
=
this
.
$fileHolder
[
0
].
querySelector
(
'
.blob-viewer:not(.hidden)
'
);
let
initialViewerName
=
initialViewer
.
getAttribute
(
'
data-type
'
);
if
(
this
.
switcher
&&
location
.
hash
.
indexOf
(
'
#L
'
)
===
0
)
{
initialViewerName
=
'
simple
'
;
}
...
...
@@ -64,40 +82,13 @@ export default class BlobViewer {
$
(
this
.
copySourceBtn
).
tooltip
(
'
fixTitle
'
);
}
loadViewer
(
viewerParam
)
{
const
viewer
=
viewerParam
;
const
url
=
viewer
.
getAttribute
(
'
data-url
'
);
if
(
!
url
||
viewer
.
getAttribute
(
'
data-loaded
'
)
||
viewer
.
getAttribute
(
'
data-loading
'
))
{
return
;
}
viewer
.
setAttribute
(
'
data-loading
'
,
'
true
'
);
$
.
ajax
({
url
,
dataType
:
'
JSON
'
,
})
.
fail
(()
=>
new
Flash
(
'
Error loading source view
'
))
.
done
((
data
)
=>
{
viewer
.
innerHTML
=
data
.
html
;
$
(
viewer
).
syntaxHighlight
();
viewer
.
setAttribute
(
'
data-loaded
'
,
'
true
'
);
this
.
$fileHolder
.
trigger
(
'
highlight:line
'
);
this
.
toggleCopyButtonState
();
});
}
switchToViewer
(
name
)
{
const
newViewer
=
document
.
querySelector
(
`.blob-viewer[data-type='
${
name
}
']`
);
const
newViewer
=
this
.
$fileHolder
[
0
]
.
querySelector
(
`.blob-viewer[data-type='
${
name
}
']`
);
if
(
this
.
activeViewer
===
newViewer
)
return
;
const
oldButton
=
document
.
querySelector
(
'
.js-blob-viewer-switch-btn.active
'
);
const
newButton
=
document
.
querySelector
(
`.js-blob-viewer-switch-btn[data-viewer='
${
name
}
']`
);
const
oldViewer
=
document
.
querySelector
(
`.blob-viewer:not([data-type='
${
name
}
'])`
);
const
oldViewer
=
this
.
$fileHolder
[
0
]
.
querySelector
(
`.blob-viewer:not([data-type='
${
name
}
'])`
);
if
(
oldButton
)
{
oldButton
.
classList
.
remove
(
'
active
'
);
...
...
@@ -118,6 +109,40 @@ export default class BlobViewer {
this
.
toggleCopyButtonState
();
this
.
loadViewer
(
newViewer
);
BlobViewer
.
loadViewer
(
newViewer
)
.
then
((
viewer
)
=>
{
$
(
viewer
).
syntaxHighlight
();
this
.
$fileHolder
.
trigger
(
'
highlight:line
'
);
this
.
toggleCopyButtonState
();
})
.
catch
(()
=>
new
Flash
(
'
Error loading viewer
'
));
}
static
loadViewer
(
viewerParam
)
{
const
viewer
=
viewerParam
;
const
url
=
viewer
.
getAttribute
(
'
data-url
'
);
return
new
Promise
((
resolve
,
reject
)
=>
{
if
(
!
url
||
viewer
.
getAttribute
(
'
data-loaded
'
)
||
viewer
.
getAttribute
(
'
data-loading
'
))
{
resolve
(
viewer
);
return
;
}
viewer
.
setAttribute
(
'
data-loading
'
,
'
true
'
);
$
.
ajax
({
url
,
dataType
:
'
JSON
'
,
})
.
fail
(
reject
)
.
done
((
data
)
=>
{
viewer
.
innerHTML
=
data
.
html
;
viewer
.
setAttribute
(
'
data-loaded
'
,
'
true
'
);
resolve
(
viewer
);
});
});
}
}
app/controllers/concerns/renders_blob.rb
View file @
cdc6d375
...
...
@@ -3,8 +3,11 @@ module RendersBlob
def
render_blob_json
(
blob
)
viewer
=
if
params
[
:viewer
]
==
'rich'
case
params
[
:viewer
]
when
'rich'
blob
.
rich_viewer
when
'auxiliary'
blob
.
auxiliary_viewer
else
blob
.
simple_viewer
end
...
...
app/helpers/projects_helper.rb
View file @
cdc6d375
...
...
@@ -110,11 +110,8 @@ def can_change_visibility_level?(project, current_user)
end
def
license_short_name
(
project
)
return
'LICENSE'
if
project
.
repository
.
license_key
.
nil?
license
=
Licensee
::
License
.
new
(
project
.
repository
.
license_key
)
license
.
nickname
||
license
.
name
license
=
project
.
repository
.
license
license
&
.
nickname
||
license
&
.
name
||
'LICENSE'
end
def
last_push_event
...
...
app/models/blob.rb
View file @
cdc6d375
...
...
@@ -34,10 +34,13 @@ class Blob < SimpleDelegator
BlobViewer
::
BinarySTL
,
BlobViewer
::
TextSTL
].
freeze
].
sort_by
{
|
v
|
v
.
binary?
?
0
:
1
}.
freeze
BINARY_VIEWERS
=
RICH_VIEWERS
.
select
(
&
:binary?
).
freeze
TEXT_VIEWERS
=
RICH_VIEWERS
.
select
(
&
:text?
).
freeze
AUXILIARY_VIEWERS
=
[
BlobViewer
::
GitlabCiYml
,
BlobViewer
::
RouteMap
,
BlobViewer
::
License
].
freeze
attr_reader
:project
...
...
@@ -154,6 +157,12 @@ def rich_viewer
@rich_viewer
=
rich_viewer_class
&
.
new
(
self
)
end
def
auxiliary_viewer
return
@auxiliary_viewer
if
defined?
(
@auxiliary_viewer
)
@auxiliary_viewer
=
auxiliary_viewer_class
&
.
new
(
self
)
end
def
rendered_as_text?
(
ignore_errors:
true
)
simple_viewer
.
text?
&&
(
ignore_errors
||
simple_viewer
.
render_error
.
nil?
)
end
...
...
@@ -180,17 +189,18 @@ def simple_viewer_class
end
def
rich_viewer_class
viewer_class_from
(
RICH_VIEWERS
)
end
def
auxiliary_viewer_class
viewer_class_from
(
AUXILIARY_VIEWERS
)
end
def
viewer_class_from
(
classes
)
return
if
empty?
||
external_storage_error?
classes
=
if
stored_externally?
BINARY_VIEWERS
+
TEXT_VIEWERS
elsif
binary?
BINARY_VIEWERS
else
# text
TEXT_VIEWERS
end
verify_binary
=
!
stored_externally?
classes
.
find
{
|
viewer_class
|
viewer_class
.
can_render?
(
self
)
}
classes
.
find
{
|
viewer_class
|
viewer_class
.
can_render?
(
self
,
verify_binary:
verify_binary
)
}
end
end
app/models/blob_viewer/auxiliary.rb
0 → 100644
View file @
cdc6d375
module
BlobViewer
module
Auxiliary
extend
ActiveSupport
::
Concern
included
do
self
.
loading_partial_name
=
'loading_auxiliary'
self
.
type
=
:auxiliary
self
.
max_size
=
100
.
kilobytes
self
.
absolute_max_size
=
100
.
kilobytes
end
end
end
app/models/blob_viewer/base.rb
View file @
cdc6d375
module
BlobViewer
class
Base
class_attribute
:partial_name
,
:type
,
:extensions
,
:client_side
,
:binary
,
:switcher_icon
,
:switcher_title
,
:max_size
,
:absolute_max_si
ze
PARTIAL_PATH_PREFIX
=
'projects/blob/viewers'
.
free
ze
delegate
:partial_path
,
:rich?
,
:simple?
,
:client_side?
,
:server_side?
,
:text?
,
:binary?
,
to: :class
class_attribute
:partial_name
,
:loading_partial_name
,
:type
,
:extensions
,
:file_type
,
:client_side
,
:binary
,
:switcher_icon
,
:switcher_title
,
:max_size
,
:absolute_max_size
self
.
loading_partial_name
=
'loading'
delegate
:partial_path
,
:loading_partial_path
,
:rich?
,
:simple?
,
:client_side?
,
:server_side?
,
:text?
,
:binary?
,
to: :class
attr_reader
:blob
attr_accessor
:override_max_size
...
...
@@ -12,7 +16,11 @@ def initialize(blob)
end
def
self
.
partial_path
"projects/blob/viewers/
#{
partial_name
}
"
File
.
join
(
PARTIAL_PATH_PREFIX
,
partial_name
)
end
def
self
.
loading_partial_path
File
.
join
(
PARTIAL_PATH_PREFIX
,
loading_partial_name
)
end
def
self
.
rich?
...
...
@@ -23,6 +31,10 @@ def self.simple?
type
==
:simple
end
def
self
.
auxiliary?
type
==
:auxiliary
end
def
self
.
client_side?
client_side
end
...
...
@@ -39,8 +51,12 @@ def self.text?
!
binary?
end
def
self
.
can_render?
(
blob
)
!
extensions
||
extensions
.
include?
(
blob
.
extension
)
def
self
.
can_render?
(
blob
,
verify_binary:
true
)
return
false
if
verify_binary
&&
binary?
!=
blob
.
binary?
return
true
if
extensions
&
.
include?
(
blob
.
extension
)
return
true
if
file_type
&&
Gitlab
::
FileDetector
.
type_of
(
blob
.
path
)
==
file_type
false
end
def
too_large?
...
...
@@ -83,9 +99,7 @@ def render_error
end
def
prepare!
if
server_side?
&&
blob
.
project
blob
.
load_all_data!
(
blob
.
project
.
repository
)
end
# To be overridden by subclasses
end
private
...
...
app/models/blob_viewer/gitlab_ci_yml.rb
0 → 100644
View file @
cdc6d375
module
BlobViewer
class
GitlabCiYml
<
Base
include
ServerSide
include
Auxiliary
self
.
partial_name
=
'gitlab_ci_yml'
self
.
loading_partial_name
=
'gitlab_ci_yml_loading'
self
.
file_type
=
:gitlab_ci
self
.
binary
=
false
def
validation_message
return
@validation_message
if
defined?
(
@validation_message
)
prepare!
@validation_message
=
Ci
::
GitlabCiYamlProcessor
.
validation_message
(
blob
.
data
)
end
def
valid?
validation_message
.
blank?
end
end
end
app/models/blob_viewer/license.rb
0 → 100644
View file @
cdc6d375
module
BlobViewer
class
License
<
Base
# We treat the License viewer as if it renders the content client-side,
# so that it doesn't attempt to load the entire blob contents and is
# rendered synchronously instead of loaded asynchronously.
include
ClientSide
include
Auxiliary
self
.
partial_name
=
'license'
self
.
file_type
=
:license
self
.
binary
=
false
def
license
blob
.
project
.
repository
.
license
end
def
render_error
return
if
license
:unknown_license
end
end
end
app/models/blob_viewer/route_map.rb
0 → 100644
View file @
cdc6d375
module
BlobViewer
class
RouteMap
<
Base
include
ServerSide
include
Auxiliary
self
.
partial_name
=
'route_map'
self
.
loading_partial_name
=
'route_map_loading'
self
.
file_type
=
:route_map
self
.
binary
=
false
def
validation_message
return
@validation_message
if
defined?
(
@validation_message
)
prepare!
@validation_message
=
begin
Gitlab
::
RouteMap
.
new
(
blob
.
data
)
nil
rescue
Gitlab
::
RouteMap
::
FormatError
=>
e
e
.
message
end
end
def
valid?
validation_message
.
blank?
end
end
end
app/models/blob_viewer/server_side.rb
View file @
cdc6d375
...
...
@@ -7,5 +7,11 @@ module ServerSide
self
.
max_size
=
2
.
megabytes
self
.
absolute_max_size
=
5
.
megabytes
end
def
prepare!
if
blob
.
project
blob
.
load_all_data!
(
blob
.
project
.
repository
)
end
end
end
end
app/models/repository.rb
View file @
cdc6d375
...
...
@@ -549,6 +549,13 @@ def license_key
end
cache_method
:license_key
def
license
return
@license
if
defined?
(
@license
)
return
unless
license_key
@license
=
Licensee
::
License
.
new
(
license_key
)
end
def
gitignore
file_on_head
(
:gitignore
)
end
...
...
@@ -1083,8 +1090,8 @@ def cache_instance_variable_name(key)
def
file_on_head
(
type
)
if
head
=
tree
(
:head
)
head
.
blobs
.
find
do
|
file
|
Gitlab
::
FileDetector
.
type_of
(
file
.
name
)
==
type
head
.
blobs
.
find
do
|
blob
|
Gitlab
::
FileDetector
.
type_of
(
blob
.
path
)
==
type
end
end
end
...
...
app/views/projects/blob/_blob.html.haml
View file @
cdc6d375
...
...
@@ -6,6 +6,11 @@
-
blob_commit
=
@repository
.
last_commit_for_path
(
@commit
.
id
,
blob
.
path
)
=
render
blob_commit
,
project:
@project
,
ref:
@ref
-
auxiliary_viewer
=
blob
.
auxiliary_viewer
-
if
auxiliary_viewer
&&
!
auxiliary_viewer
.
render_error
.well-segment.blob-auxiliary-viewer
=
render
'projects/blob/viewer'
,
viewer:
auxiliary_viewer
#blob-content-holder
.blob-content-holder
%article
.file-holder
=
render
"projects/blob/header"
,
blob:
blob
...
...
app/views/projects/blob/_viewer.html.haml
View file @
cdc6d375
...
...
@@ -5,8 +5,7 @@
-
viewer_url
=
local_assigns
.
fetch
(
:viewer_url
)
{
url_for
(
params
.
merge
(
viewer:
viewer
.
type
,
format: :json
))
}
if
load_asynchronously
.blob-viewer
{
data:
{
type:
viewer
.
type
,
url:
viewer_url
},
class:
(
'hidden'
if
hidden
)
}
-
if
load_asynchronously
.text-center.prepend-top-default.append-bottom-default
=
icon
(
'spinner spin 2x'
,
'aria-hidden'
=>
'true'
,
'aria-label'
=>
'Loading content'
)
=
render
viewer
.
loading_partial_path
,
viewer:
viewer
-
elsif
render_error
=
render
'projects/blob/render_error'
,
viewer:
viewer
-
else
...
...
app/views/projects/blob/viewers/_gitlab_ci_yml.html.haml
0 → 100644
View file @
cdc6d375
-
if
viewer
.
valid?
=
icon
(
'check fw'
)
This GitLab CI configuration is valid.
-
else
=
icon
(
'warning fw'
)
This GitLab CI configuration is invalid:
=
viewer
.
validation_message
=
link_to
'Learn more'
,
help_page_path
(
'ci/yaml/README'
)
app/views/projects/blob/viewers/_gitlab_ci_yml_loading.html.haml
0 → 100644
View file @
cdc6d375
=
icon
(
'spinner spin fw'
)
Validating GitLab CI configuration…
=
link_to
'Learn more'
,
help_page_path
(
'ci/yaml/README'
)
app/views/projects/blob/viewers/_license.html.haml
0 → 100644
View file @
cdc6d375
-
license
=
viewer
.
license
=
icon
(
'balance-scale fw'
)
This project is licensed under the
=
succeed
'.'
do
%strong
=
license
.
name
=
link_to
'Learn more about this license'
,
license
.
url
,
target:
'_blank'
,
rel:
'noopener noreferrer'
app/views/projects/blob/viewers/_loading.html.haml
0 → 100644
View file @
cdc6d375
.text-center.prepend-top-default.append-bottom-default
=
icon
(
'spinner spin 2x'
,
'aria-hidden'
=>
'true'
,
'aria-label'
=>
'Loading content…'
)
app/views/projects/blob/viewers/_loading_auxiliary.html.haml
0 → 100644
View file @
cdc6d375
=
icon
(
'spinner spin fw'
)
Loading…
app/views/projects/blob/viewers/_route_map.html.haml
0 → 100644
View file @
cdc6d375
-
if
viewer
.
valid?
=
icon
(
'check fw'
)
This Route Map is valid.
-
else
=
icon
(
'warning fw'
)
This Route Map is invalid:
=
viewer
.
validation_message
=
link_to
'Learn more'
,
help_page_path
(
'ci/environments'
,
anchor:
'route-map'
)
app/views/projects/blob/viewers/_route_map_loading.html.haml
0 → 100644
View file @
cdc6d375
=
icon
(
'spinner spin fw'
)
Validating Route Map…
=
link_to
'Learn more'
,
help_page_path
(
'ci/environments'
,
anchor:
'route-map'
)
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment