Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F110313443
PhabricatorProjectTransactionEditor.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
Fri, Apr 25, 17:47
Size
16 KB
Mime Type
text/x-php
Expires
Sun, Apr 27, 17:47 (2 d)
Engine
blob
Format
Raw Data
Handle
25782845
Attached To
rPH Phabricator
PhabricatorProjectTransactionEditor.php
View Options
<?php
final
class
PhabricatorProjectTransactionEditor
extends
PhabricatorApplicationTransactionEditor
{
public
function
getEditorApplicationClass
()
{
return
'PhabricatorProjectApplication'
;
}
public
function
getEditorObjectsDescription
()
{
return
pht
(
'Projects'
);
}
public
function
getTransactionTypes
()
{
$types
=
parent
::
getTransactionTypes
();
$types
[]
=
PhabricatorTransactions
::
TYPE_EDGE
;
$types
[]
=
PhabricatorTransactions
::
TYPE_VIEW_POLICY
;
$types
[]
=
PhabricatorTransactions
::
TYPE_EDIT_POLICY
;
$types
[]
=
PhabricatorTransactions
::
TYPE_JOIN_POLICY
;
$types
[]
=
PhabricatorProjectTransaction
::
TYPE_NAME
;
$types
[]
=
PhabricatorProjectTransaction
::
TYPE_SLUGS
;
$types
[]
=
PhabricatorProjectTransaction
::
TYPE_STATUS
;
$types
[]
=
PhabricatorProjectTransaction
::
TYPE_IMAGE
;
$types
[]
=
PhabricatorProjectTransaction
::
TYPE_ICON
;
$types
[]
=
PhabricatorProjectTransaction
::
TYPE_COLOR
;
return
$types
;
}
protected
function
getCustomTransactionOldValue
(
PhabricatorLiskDAO
$object
,
PhabricatorApplicationTransaction
$xaction
)
{
switch
(
$xaction
->
getTransactionType
())
{
case
PhabricatorProjectTransaction
::
TYPE_NAME
:
return
$object
->
getName
();
case
PhabricatorProjectTransaction
::
TYPE_SLUGS
:
$slugs
=
$object
->
getSlugs
();
$slugs
=
mpull
(
$slugs
,
'getSlug'
,
'getSlug'
);
unset
(
$slugs
[
$object
->
getPrimarySlug
()]);
return
$slugs
;
case
PhabricatorProjectTransaction
::
TYPE_STATUS
:
return
$object
->
getStatus
();
case
PhabricatorProjectTransaction
::
TYPE_IMAGE
:
return
$object
->
getProfileImagePHID
();
case
PhabricatorProjectTransaction
::
TYPE_ICON
:
return
$object
->
getIcon
();
case
PhabricatorProjectTransaction
::
TYPE_COLOR
:
return
$object
->
getColor
();
}
return
parent
::
getCustomTransactionOldValue
(
$object
,
$xaction
);
}
protected
function
getCustomTransactionNewValue
(
PhabricatorLiskDAO
$object
,
PhabricatorApplicationTransaction
$xaction
)
{
switch
(
$xaction
->
getTransactionType
())
{
case
PhabricatorProjectTransaction
::
TYPE_NAME
:
case
PhabricatorProjectTransaction
::
TYPE_SLUGS
:
case
PhabricatorProjectTransaction
::
TYPE_STATUS
:
case
PhabricatorProjectTransaction
::
TYPE_IMAGE
:
case
PhabricatorProjectTransaction
::
TYPE_ICON
:
case
PhabricatorProjectTransaction
::
TYPE_COLOR
:
return
$xaction
->
getNewValue
();
}
return
parent
::
getCustomTransactionNewValue
(
$object
,
$xaction
);
}
protected
function
applyCustomInternalTransaction
(
PhabricatorLiskDAO
$object
,
PhabricatorApplicationTransaction
$xaction
)
{
switch
(
$xaction
->
getTransactionType
())
{
case
PhabricatorProjectTransaction
::
TYPE_NAME
:
$object
->
setName
(
$xaction
->
getNewValue
());
$object
->
setPhrictionSlug
(
$xaction
->
getNewValue
());
return
;
case
PhabricatorProjectTransaction
::
TYPE_SLUGS
:
return
;
case
PhabricatorProjectTransaction
::
TYPE_STATUS
:
$object
->
setStatus
(
$xaction
->
getNewValue
());
return
;
case
PhabricatorProjectTransaction
::
TYPE_IMAGE
:
$object
->
setProfileImagePHID
(
$xaction
->
getNewValue
());
return
;
case
PhabricatorProjectTransaction
::
TYPE_ICON
:
$object
->
setIcon
(
$xaction
->
getNewValue
());
return
;
case
PhabricatorProjectTransaction
::
TYPE_COLOR
:
$object
->
setColor
(
$xaction
->
getNewValue
());
return
;
case
PhabricatorTransactions
::
TYPE_EDGE
:
return
;
case
PhabricatorTransactions
::
TYPE_VIEW_POLICY
:
$object
->
setViewPolicy
(
$xaction
->
getNewValue
());
return
;
case
PhabricatorTransactions
::
TYPE_EDIT_POLICY
:
$object
->
setEditPolicy
(
$xaction
->
getNewValue
());
return
;
case
PhabricatorTransactions
::
TYPE_JOIN_POLICY
:
$object
->
setJoinPolicy
(
$xaction
->
getNewValue
());
return
;
}
return
parent
::
applyCustomInternalTransaction
(
$object
,
$xaction
);
}
protected
function
applyCustomExternalTransaction
(
PhabricatorLiskDAO
$object
,
PhabricatorApplicationTransaction
$xaction
)
{
$old
=
$xaction
->
getOldValue
();
$new
=
$xaction
->
getNewValue
();
switch
(
$xaction
->
getTransactionType
())
{
case
PhabricatorProjectTransaction
::
TYPE_NAME
:
// First, remove the old and new slugs. Removing the old slug is
// important when changing the project's capitalization or puctuation.
// Removing the new slug is important when changing the project's name
// so that one of its secondary slugs is now the primary slug.
if
(
$old
!==
null
)
{
$this
->
removeSlug
(
$object
,
$old
);
}
$this
->
removeSlug
(
$object
,
$new
);
$new_slug
=
id
(
new
PhabricatorProjectSlug
())
->
setSlug
(
$object
->
getPrimarySlug
())
->
setProjectPHID
(
$object
->
getPHID
())
->
save
();
$object
->
updateDatasourceTokens
();
// TODO -- delete all of the below once we sever automagical project
// to phriction stuff
if
(
$xaction
->
getOldValue
()
===
null
)
{
// Project was just created, we don't need to move anything.
return
;
}
$clone_object
=
clone
$object
;
$clone_object
->
setPhrictionSlug
(
$xaction
->
getOldValue
());
$old_slug
=
$clone_object
->
getFullPhrictionSlug
();
$old_document
=
id
(
new
PhrictionDocument
())
->
loadOneWhere
(
'slug = %s'
,
$old_slug
);
if
(
$old_document
&&
$old_document
->
getStatus
()
==
PhrictionDocumentStatus
::
STATUS_EXISTS
)
{
$content
=
id
(
new
PhrictionContent
())
->
load
(
$old_document
->
getContentID
());
$from_editor
=
id
(
PhrictionDocumentEditor
::
newForSlug
(
$old_slug
))
->
setActor
(
$this
->
getActor
())
->
setTitle
(
$content
->
getTitle
())
->
setContent
(
$content
->
getContent
())
->
setDescription
(
$content
->
getDescription
());
$target_editor
=
id
(
PhrictionDocumentEditor
::
newForSlug
(
$object
->
getFullPhrictionSlug
()))
->
setActor
(
$this
->
getActor
())
->
setTitle
(
$content
->
getTitle
())
->
setContent
(
$content
->
getContent
())
->
setDescription
(
$content
->
getDescription
())
->
moveHere
(
$old_document
->
getID
(),
$old_document
->
getPHID
());
$target_document
=
$target_editor
->
getDocument
();
$from_editor
->
moveAway
(
$target_document
->
getID
());
}
return
;
case
PhabricatorProjectTransaction
::
TYPE_SLUGS
:
$old
=
$xaction
->
getOldValue
();
$new
=
$xaction
->
getNewValue
();
$add
=
array_diff
(
$new
,
$old
);
$rem
=
array_diff
(
$old
,
$new
);
if
(
$add
)
{
$add_slug_template
=
id
(
new
PhabricatorProjectSlug
())
->
setProjectPHID
(
$object
->
getPHID
());
foreach
(
$add
as
$add_slug_str
)
{
$add_slug
=
id
(
clone
$add_slug_template
)
->
setSlug
(
$add_slug_str
)
->
save
();
}
}
if
(
$rem
)
{
$rem_slugs
=
id
(
new
PhabricatorProjectSlug
())
->
loadAllWhere
(
'slug IN (%Ls)'
,
$rem
);
foreach
(
$rem_slugs
as
$rem_slug
)
{
$rem_slug
->
delete
();
}
}
$object
->
updateDatasourceTokens
();
return
;
case
PhabricatorTransactions
::
TYPE_VIEW_POLICY
:
case
PhabricatorTransactions
::
TYPE_EDIT_POLICY
:
case
PhabricatorTransactions
::
TYPE_JOIN_POLICY
:
case
PhabricatorProjectTransaction
::
TYPE_STATUS
:
case
PhabricatorProjectTransaction
::
TYPE_IMAGE
:
case
PhabricatorProjectTransaction
::
TYPE_ICON
:
case
PhabricatorProjectTransaction
::
TYPE_COLOR
:
return
;
case
PhabricatorTransactions
::
TYPE_EDGE
:
$edge_type
=
$xaction
->
getMetadataValue
(
'edge:type'
);
switch
(
$edge_type
)
{
case
PhabricatorEdgeConfig
::
TYPE_PROJ_MEMBER
:
case
PhabricatorEdgeConfig
::
TYPE_OBJECT_HAS_WATCHER
:
$old
=
$xaction
->
getOldValue
();
$new
=
$xaction
->
getNewValue
();
// When adding members or watchers, we add subscriptions.
$add
=
array_keys
(
array_diff_key
(
$new
,
$old
));
// When removing members, we remove their subscription too.
// When unwatching, we leave subscriptions, since it's fine to be
// subscribed to a project but not be a member of it.
if
(
$edge_type
==
PhabricatorEdgeConfig
::
TYPE_PROJ_MEMBER
)
{
$rem
=
array_keys
(
array_diff_key
(
$old
,
$new
));
}
else
{
$rem
=
array
();
}
// NOTE: The subscribe is "explicit" because there's no implicit
// unsubscribe, so Join -> Leave -> Join doesn't resubscribe you
// if we use an implicit subscribe, even though you never willfully
// unsubscribed. Not sure if adding implicit unsubscribe (which
// would not write the unsubscribe row) is justified to deal with
// this, which is a fairly weird edge case and pretty arguable both
// ways.
// Subscriptions caused by watches should also clearly be explicit,
// and that case is unambiguous.
id
(
new
PhabricatorSubscriptionsEditor
())
->
setActor
(
$this
->
requireActor
())
->
setObject
(
$object
)
->
subscribeExplicit
(
$add
)
->
unsubscribe
(
$rem
)
->
save
();
if
(
$rem
)
{
// When removing members, also remove any watches on the project.
$edge_editor
=
new
PhabricatorEdgeEditor
();
foreach
(
$rem
as
$rem_phid
)
{
$edge_editor
->
removeEdge
(
$object
->
getPHID
(),
PhabricatorEdgeConfig
::
TYPE_OBJECT_HAS_WATCHER
,
$rem_phid
);
}
$edge_editor
->
save
();
}
break
;
}
return
;
}
return
parent
::
applyCustomExternalTransaction
(
$object
,
$xaction
);
}
protected
function
validateTransaction
(
PhabricatorLiskDAO
$object
,
$type
,
array
$xactions
)
{
$errors
=
parent
::
validateTransaction
(
$object
,
$type
,
$xactions
);
switch
(
$type
)
{
case
PhabricatorProjectTransaction
::
TYPE_NAME
:
$missing
=
$this
->
validateIsEmptyTextField
(
$object
->
getName
(),
$xactions
);
if
(
$missing
)
{
$error
=
new
PhabricatorApplicationTransactionValidationError
(
$type
,
pht
(
'Required'
),
pht
(
'Project name is required.'
),
nonempty
(
last
(
$xactions
),
null
));
$error
->
setIsMissingFieldError
(
true
);
$errors
[]
=
$error
;
}
if
(!
$xactions
)
{
break
;
}
$name
=
last
(
$xactions
)->
getNewValue
();
$name_used_already
=
id
(
new
PhabricatorProjectQuery
())
->
setViewer
(
$this
->
getActor
())
->
withNames
(
array
(
$name
))
->
executeOne
();
if
(
$name_used_already
&&
(
$name_used_already
->
getPHID
()
!=
$object
->
getPHID
()))
{
$error
=
new
PhabricatorApplicationTransactionValidationError
(
$type
,
pht
(
'Duplicate'
),
pht
(
'Project name is already used.'
),
nonempty
(
last
(
$xactions
),
null
));
$errors
[]
=
$error
;
}
$slug_builder
=
clone
$object
;
$slug_builder
->
setPhrictionSlug
(
$name
);
$slug
=
$slug_builder
->
getPrimarySlug
();
$slug_used_already
=
id
(
new
PhabricatorProjectSlug
())
->
loadOneWhere
(
'slug = %s'
,
$slug
);
if
(
$slug_used_already
&&
$slug_used_already
->
getProjectPHID
()
!=
$object
->
getPHID
())
{
$error
=
new
PhabricatorApplicationTransactionValidationError
(
$type
,
pht
(
'Duplicate'
),
pht
(
'Project name can not be used due to hashtag collision.'
),
nonempty
(
last
(
$xactions
),
null
));
$errors
[]
=
$error
;
}
break
;
case
PhabricatorProjectTransaction
::
TYPE_SLUGS
:
if
(!
$xactions
)
{
break
;
}
$slug_xaction
=
last
(
$xactions
);
$new
=
$slug_xaction
->
getNewValue
();
if
(
$new
)
{
$slugs_used_already
=
id
(
new
PhabricatorProjectSlug
())
->
loadAllWhere
(
'slug IN (%Ls)'
,
$new
);
}
else
{
// The project doesn't have any extra slugs.
$slugs_used_already
=
array
();
}
$slugs_used_already
=
mgroup
(
$slugs_used_already
,
'getProjectPHID'
);
foreach
(
$slugs_used_already
as
$project_phid
=>
$used_slugs
)
{
$used_slug_strs
=
mpull
(
$used_slugs
,
'getSlug'
);
if
(
$project_phid
==
$object
->
getPHID
())
{
if
(
in_array
(
$object
->
getPrimarySlug
(),
$used_slug_strs
))
{
$error
=
new
PhabricatorApplicationTransactionValidationError
(
$type
,
pht
(
'Invalid'
),
pht
(
'Project hashtag %s is already the primary hashtag.'
,
$object
->
getPrimarySlug
()),
$slug_xaction
);
$errors
[]
=
$error
;
}
continue
;
}
$error
=
new
PhabricatorApplicationTransactionValidationError
(
$type
,
pht
(
'Invalid'
),
pht
(
'%d project hashtag(s) are already used: %s'
,
count
(
$used_slug_strs
),
implode
(
', '
,
$used_slug_strs
)),
$slug_xaction
);
$errors
[]
=
$error
;
}
break
;
}
return
$errors
;
}
protected
function
requireCapabilities
(
PhabricatorLiskDAO
$object
,
PhabricatorApplicationTransaction
$xaction
)
{
switch
(
$xaction
->
getTransactionType
())
{
case
PhabricatorProjectTransaction
::
TYPE_NAME
:
case
PhabricatorProjectTransaction
::
TYPE_STATUS
:
case
PhabricatorProjectTransaction
::
TYPE_IMAGE
:
case
PhabricatorProjectTransaction
::
TYPE_ICON
:
case
PhabricatorProjectTransaction
::
TYPE_COLOR
:
PhabricatorPolicyFilter
::
requireCapability
(
$this
->
requireActor
(),
$object
,
PhabricatorPolicyCapability
::
CAN_EDIT
);
return
;
case
PhabricatorTransactions
::
TYPE_EDGE
:
switch
(
$xaction
->
getMetadataValue
(
'edge:type'
))
{
case
PhabricatorEdgeConfig
::
TYPE_PROJ_MEMBER
:
$old
=
$xaction
->
getOldValue
();
$new
=
$xaction
->
getNewValue
();
$add
=
array_keys
(
array_diff_key
(
$new
,
$old
));
$rem
=
array_keys
(
array_diff_key
(
$old
,
$new
));
$actor_phid
=
$this
->
requireActor
()->
getPHID
();
$is_join
=
((
$add
===
array
(
$actor_phid
))
&&
!
$rem
);
$is_leave
=
((
$rem
===
array
(
$actor_phid
))
&&
!
$add
);
if
(
$is_join
)
{
// You need CAN_JOIN to join a project.
PhabricatorPolicyFilter
::
requireCapability
(
$this
->
requireActor
(),
$object
,
PhabricatorPolicyCapability
::
CAN_JOIN
);
}
else
if
(
$is_leave
)
{
// You don't need any capabilities to leave a project.
}
else
{
// You need CAN_EDIT to change members other than yourself.
PhabricatorPolicyFilter
::
requireCapability
(
$this
->
requireActor
(),
$object
,
PhabricatorPolicyCapability
::
CAN_EDIT
);
}
return
;
}
break
;
}
return
parent
::
requireCapabilities
(
$object
,
$xaction
);
}
protected
function
supportsSearch
()
{
return
true
;
}
protected
function
extractFilePHIDsFromCustomTransaction
(
PhabricatorLiskDAO
$object
,
PhabricatorApplicationTransaction
$xaction
)
{
switch
(
$xaction
->
getTransactionType
())
{
case
PhabricatorProjectTransaction
::
TYPE_IMAGE
:
$new
=
$xaction
->
getNewValue
();
if
(
$new
)
{
return
array
(
$new
);
}
break
;
}
return
parent
::
extractFilePHIDsFromCustomTransaction
(
$object
,
$xaction
);
}
private
function
removeSlug
(
PhabricatorLiskDAO
$object
,
$name
)
{
$object
=
(
clone
$object
);
$object
->
setPhrictionSlug
(
$name
);
$slug
=
$object
->
getPrimarySlug
();
$slug_object
=
id
(
new
PhabricatorProjectSlug
())->
loadOneWhere
(
'slug = %s'
,
$slug
);
if
(!
$slug_object
)
{
return
;
}
if
(
$slug_object
->
getProjectPHID
()
!=
$object
->
getPHID
())
{
throw
new
Exception
(
pht
(
'Trying to remove slug owned by another project!'
));
}
$slug_object
->
delete
();
}
}
Event Timeline
Log In to Comment