Открыть боковую панель
nt_test133
nt_project_uxpg8lvcuq8w
Коммиты
1c75e4e6
Коммит
1c75e4e6
создал
Май 11, 2017
по автору
Sean McGivern
Просмотр файлов
Merge branch 'dm-auxiliary-viewers' into 'master'
Implement auxiliary blob viewers See merge request !11195
владельцы
4067dd4f
90997505
Изменения
32
Скрыть пробелы
Построчно
Рядом
app/assets/javascripts/blob/viewer/index.js
Просмотр файла @
1c75e4e6
/* 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
Просмотр файла @
1c75e4e6
...
...
@@ -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
Просмотр файла @
1c75e4e6
...
...
@@ -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
Просмотр файла @
1c75e4e6
...
...
@@ -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
Просмотр файла @
1c75e4e6
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
Просмотр файла @
1c75e4e6
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
Просмотр файла @
1c75e4e6
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
Просмотр файла @
1c75e4e6
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
Просмотр файла @
1c75e4e6
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
Просмотр файла @
1c75e4e6
...
...
@@ -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
Просмотр файла @
1c75e4e6
...
...
@@ -30,7 +30,7 @@ class Repository
METHOD_CACHES_FOR_FILE_TYPES
=
{
readme: :rendered_readme
,
changelog: :changelog
,
license:
%i(license_blob license_key)
,
license:
%i(license_blob license_key
license
)
,
contributing: :contribution_guide
,
gitignore: :gitignore
,
koding: :koding_yml
,
...
...
@@ -42,13 +42,13 @@ class Repository
# variable.
#
# This only works for methods that do not take any arguments.
def
self
.
cache_method
(
name
,
fallback:
nil
)
def
self
.
cache_method
(
name
,
fallback:
nil
,
memoize_only:
false
)
original
=
:"_uncached_
#{
name
}
"
alias_method
(
original
,
name
)
define_method
(
name
)
do
cache_method_output
(
name
,
fallback:
fallback
)
{
__send__
(
original
)
}
cache_method_output
(
name
,
fallback:
fallback
,
memoize_only:
memoize_only
)
{
__send__
(
original
)
}
end
end
...
...
@@ -549,6 +549,13 @@ def license_key
end
cache_method
:license_key
def
license
return
unless
license_key
Licensee
::
License
.
new
(
license_key
)
end
cache_method
:license
,
memoize_only:
true
def
gitignore
file_on_head
(
:gitignore
)
end
...
...
@@ -1061,14 +1068,20 @@ def copy_gitattributes(ref)
#
# key - The name of the key to cache the data in.
# fallback - A value to fall back to in the event of a Git error.
def
cache_method_output
(
key
,
fallback:
nil
,
&
block
)
def
cache_method_output
(
key
,
fallback:
nil
,
memoize_only:
false
,
&
block
)
ivar
=
cache_instance_variable_name
(
key
)
if
instance_variable_defined?
(
ivar
)
instance_variable_get
(
ivar
)
else
begin
instance_variable_set
(
ivar
,
cache
.
fetch
(
key
,
&
block
))
value
=
if
memoize_only
yield
else
cache
.
fetch
(
key
,
&
block
)
end
instance_variable_set
(
ivar
,
value
)
rescue
Rugged
::
ReferenceError
,
Gitlab
::
Git
::
Repository
::
NoRepository
# if e.g. HEAD or the entire repository doesn't exist we want to
# gracefully handle this and not cache anything.
...
...
@@ -1083,8 +1096,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
Просмотр файла @
1c75e4e6
...
...
@@ -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
Просмотр файла @
1c75e4e6
...
...
@@ -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
Просмотр файла @
1c75e4e6
-
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
Просмотр файла @
1c75e4e6
=
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
Просмотр файла @
1c75e4e6
-
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
Просмотр файла @
1c75e4e6
.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
Просмотр файла @
1c75e4e6
=
icon
(
'spinner spin fw'
)
Loading…
app/views/projects/blob/viewers/_route_map.html.haml
0 → 100644
Просмотр файла @
1c75e4e6
-
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
Просмотр файла @
1c75e4e6
=
icon
(
'spinner spin fw'
)
Validating Route Map…
=
link_to
'Learn more'
,
help_page_path
(
'ci/environments'
,
anchor:
'route-map'
)
Пред
1
2
След
Редактирование
Предварительный просмотр
Поддерживает Markdown
0%
Попробовать снова
или
прикрепить новый файл
.
Отмена
You are about to add
0
people
to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Отмена
Пожалуйста,
зарегистрируйтесь
или
войдите
чтобы прокомментировать