Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F98489843
CelerityStaticResourceResponse.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Jan 13, 16:07
Size
8 KB
Mime Type
text/x-php
Expires
Wed, Jan 15, 16:07 (2 d)
Engine
blob
Format
Raw Data
Handle
23593095
Attached To
rPH Phabricator
CelerityStaticResourceResponse.php
View Options
<?php
/**
* Tracks and resolves dependencies the page declares with
* @{function:require_celerity_resource}, and then builds appropriate HTML or
* Ajax responses.
*/
final
class
CelerityStaticResourceResponse
{
private
$symbols
=
array
();
private
$needsResolve
=
true
;
private
$resolved
;
private
$packaged
;
private
$metadata
=
array
();
private
$metadataBlock
=
0
;
private
$behaviors
=
array
();
private
$hasRendered
=
array
();
public
function
__construct
()
{
if
(
isset
(
$_REQUEST
[
'__metablock__'
]))
{
$this
->
metadataBlock
=
(
int
)
$_REQUEST
[
'__metablock__'
];
}
}
public
function
addMetadata
(
$metadata
)
{
$id
=
count
(
$this
->
metadata
);
$this
->
metadata
[
$id
]
=
$metadata
;
return
$this
->
metadataBlock
.
'_'
.
$id
;
}
public
function
getMetadataBlock
()
{
return
$this
->
metadataBlock
;
}
/**
* Register a behavior for initialization. NOTE: if $config is empty,
* a behavior will execute only once even if it is initialized multiple times.
* If $config is nonempty, the behavior will be invoked once for each config.
*/
public
function
initBehavior
(
$behavior
,
array
$config
=
array
(),
$source_name
)
{
$this
->
requireResource
(
'javelin-behavior-'
.
$behavior
,
$source_name
);
if
(
empty
(
$this
->
behaviors
[
$behavior
]))
{
$this
->
behaviors
[
$behavior
]
=
array
();
}
if
(
$config
)
{
$this
->
behaviors
[
$behavior
][]
=
$config
;
}
return
$this
;
}
public
function
requireResource
(
$symbol
,
$source_name
)
{
if
(
isset
(
$this
->
symbols
[
$source_name
][
$symbol
]))
{
return
$this
;
}
// Verify that the resource exists.
$map
=
CelerityResourceMap
::
getNamedInstance
(
$source_name
);
$name
=
$map
->
getResourceNameForSymbol
(
$symbol
);
if
(
$name
===
null
)
{
throw
new
Exception
(
pht
(
'No resource with symbol "%s" exists in source "%s"!'
,
$symbol
,
$source_name
));
}
$this
->
symbols
[
$source_name
][
$symbol
]
=
true
;
$this
->
needsResolve
=
true
;
return
$this
;
}
private
function
resolveResources
()
{
if
(
$this
->
needsResolve
)
{
$this
->
packaged
=
array
();
foreach
(
$this
->
symbols
as
$source_name
=>
$symbols_map
)
{
$symbols
=
array_keys
(
$symbols_map
);
$map
=
CelerityResourceMap
::
getNamedInstance
(
$source_name
);
$packaged
=
$map
->
getPackagedNamesForSymbols
(
$symbols
);
$this
->
packaged
[
$source_name
]
=
$packaged
;
}
$this
->
needsResolve
=
false
;
}
return
$this
;
}
public
function
renderSingleResource
(
$symbol
,
$source_name
)
{
$map
=
CelerityResourceMap
::
getNamedInstance
(
$source_name
);
$packaged
=
$map
->
getPackagedNamesForSymbols
(
array
(
$symbol
));
return
$this
->
renderPackagedResources
(
$map
,
$packaged
);
}
public
function
renderResourcesOfType
(
$type
)
{
$this
->
resolveResources
();
$result
=
array
();
foreach
(
$this
->
packaged
as
$source_name
=>
$resource_names
)
{
$map
=
CelerityResourceMap
::
getNamedInstance
(
$source_name
);
$resources_of_type
=
array
();
foreach
(
$resource_names
as
$resource_name
)
{
$resource_type
=
$map
->
getResourceTypeForName
(
$resource_name
);
if
(
$resource_type
==
$type
)
{
$resources_of_type
[]
=
$resource_name
;
}
}
$result
[]
=
$this
->
renderPackagedResources
(
$map
,
$resources_of_type
);
}
return
phutil_implode_html
(
''
,
$result
);
}
private
function
renderPackagedResources
(
CelerityResourceMap
$map
,
array
$resources
)
{
$output
=
array
();
foreach
(
$resources
as
$name
)
{
if
(
isset
(
$this
->
hasRendered
[
$name
]))
{
continue
;
}
$this
->
hasRendered
[
$name
]
=
true
;
$output
[]
=
$this
->
renderResource
(
$map
,
$name
);
}
return
$output
;
}
private
function
renderResource
(
CelerityResourceMap
$map
,
$name
)
{
$uri
=
$this
->
getURI
(
$map
,
$name
);
$type
=
$map
->
getResourceTypeForName
(
$name
);
$multimeter
=
MultimeterControl
::
getInstance
();
if
(
$multimeter
)
{
$event_type
=
MultimeterEvent
::
TYPE_STATIC_RESOURCE
;
$multimeter
->
newEvent
(
$event_type
,
'rsrc.'
.
$name
,
1
);
}
switch
(
$type
)
{
case
'css'
:
return
phutil_tag
(
'link'
,
array
(
'rel'
=>
'stylesheet'
,
'type'
=>
'text/css'
,
'href'
=>
$uri
,
));
case
'js'
:
return
phutil_tag
(
'script'
,
array
(
'type'
=>
'text/javascript'
,
'src'
=>
$uri
,
),
''
);
}
throw
new
Exception
(
pht
(
'Unable to render resource "%s", which has unknown type "%s".'
,
$name
,
$type
));
}
public
function
renderHTMLFooter
()
{
$data
=
array
();
if
(
$this
->
metadata
)
{
$json_metadata
=
AphrontResponse
::
encodeJSONForHTTPResponse
(
$this
->
metadata
);
$this
->
metadata
=
array
();
}
else
{
$json_metadata
=
'{}'
;
}
// Even if there is no metadata on the page, Javelin uses the mergeData()
// call to start dispatching the event queue.
$data
[]
=
'JX.Stratcom.mergeData('
.
$this
->
metadataBlock
.
', '
.
$json_metadata
.
');'
;
$onload
=
array
();
if
(
$this
->
behaviors
)
{
$behaviors
=
$this
->
behaviors
;
$this
->
behaviors
=
array
();
$higher_priority_names
=
array
(
'refresh-csrf'
,
'aphront-basic-tokenizer'
,
'dark-console'
,
'history-install'
,
);
$higher_priority_behaviors
=
array_select_keys
(
$behaviors
,
$higher_priority_names
);
foreach
(
$higher_priority_names
as
$name
)
{
unset
(
$behaviors
[
$name
]);
}
$behavior_groups
=
array
(
$higher_priority_behaviors
,
$behaviors
,
);
foreach
(
$behavior_groups
as
$group
)
{
if
(!
$group
)
{
continue
;
}
$group_json
=
AphrontResponse
::
encodeJSONForHTTPResponse
(
$group
);
$onload
[]
=
'JX.initBehaviors('
.
$group_json
.
')'
;
}
}
if
(
$onload
)
{
foreach
(
$onload
as
$func
)
{
$data
[]
=
'JX.onload(function(){'
.
$func
.
'});'
;
}
}
if
(
$data
)
{
$data
=
implode
(
"
\n
"
,
$data
);
return
self
::
renderInlineScript
(
$data
);
}
else
{
return
''
;
}
}
public
static
function
renderInlineScript
(
$data
)
{
if
(
stripos
(
$data
,
'</script>'
)
!==
false
)
{
throw
new
Exception
(
'Literal </script> is not allowed inside inline script.'
);
}
if
(
strpos
(
$data
,
'<!'
)
!==
false
)
{
throw
new
Exception
(
'Literal <! is not allowed inside inline script.'
);
}
// We don't use <![CDATA[ ]]> because it is ignored by HTML parsers. We
// would need to send the document with XHTML content type.
return
phutil_tag
(
'script'
,
array
(
'type'
=>
'text/javascript'
),
phutil_safe_html
(
$data
));
}
public
function
buildAjaxResponse
(
$payload
,
$error
=
null
)
{
$response
=
array
(
'error'
=>
$error
,
'payload'
=>
$payload
,
);
if
(
$this
->
metadata
)
{
$response
[
'javelin_metadata'
]
=
$this
->
metadata
;
$this
->
metadata
=
array
();
}
if
(
$this
->
behaviors
)
{
$response
[
'javelin_behaviors'
]
=
$this
->
behaviors
;
$this
->
behaviors
=
array
();
}
$this
->
resolveResources
();
$resources
=
array
();
foreach
(
$this
->
packaged
as
$source_name
=>
$resource_names
)
{
$map
=
CelerityResourceMap
::
getNamedInstance
(
$source_name
);
foreach
(
$resource_names
as
$resource_name
)
{
$resources
[]
=
$this
->
getURI
(
$map
,
$resource_name
);
}
}
if
(
$resources
)
{
$response
[
'javelin_resources'
]
=
$resources
;
}
return
$response
;
}
public
function
getURI
(
CelerityResourceMap
$map
,
$name
,
$use_primary_domain
=
false
)
{
$uri
=
$map
->
getURIForName
(
$name
);
// In developer mode, we dump file modification times into the URI. When a
// page is reloaded in the browser, any resources brought in by Ajax calls
// do not trigger revalidation, so without this it's very difficult to get
// changes to Ajaxed-in CSS to work (you must clear your cache or rerun
// the map script). In production, we can assume the map script gets run
// after changes, and safely skip this.
if
(
PhabricatorEnv
::
getEnvConfig
(
'phabricator.developer-mode'
))
{
$mtime
=
$map
->
getModifiedTimeForName
(
$name
);
$uri
=
preg_replace
(
'@^/res/@'
,
'/res/'
.
$mtime
.
'T/'
,
$uri
);
}
if
(
$use_primary_domain
)
{
return
PhabricatorEnv
::
getURI
(
$uri
);
}
else
{
return
PhabricatorEnv
::
getCDNURI
(
$uri
);
}
}
}
Event Timeline
Log In to Comment