Merge branch 'github-facebook-master' into blender-tweaks
Before Width: | Height: | Size: 993 B After Width: | Height: | Size: 293 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 360 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 375 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 370 B |
BIN
resources/builtin/image-400x400.png
Normal file
After Width: | Height: | Size: 456 B |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 546 B |
BIN
resources/builtin/image-800x800.png
Normal file
After Width: | Height: | Size: 666 B |
@@ -46,6 +46,7 @@ return array(
|
||||
'javelin-behavior-toggle-class',
|
||||
'javelin-behavior-lightbox-attachments',
|
||||
'phabricator-busy',
|
||||
'javelin-sound',
|
||||
'javelin-aphlict',
|
||||
'phabricator-notification',
|
||||
'javelin-behavior-aphlict-listen',
|
||||
@@ -138,7 +139,6 @@ return array(
|
||||
|
||||
'font-fontawesome',
|
||||
'font-lato',
|
||||
'font-aleo',
|
||||
'phui-font-icon-base-css',
|
||||
'phui-fontkit-css',
|
||||
'phui-box-css',
|
||||
@@ -161,6 +161,7 @@ return array(
|
||||
'conpherence.pkg.css' => array(
|
||||
'conpherence-durable-column-view',
|
||||
'conpherence-menu-css',
|
||||
'conpherence-color-css',
|
||||
'conpherence-message-pane-css',
|
||||
'conpherence-notification-css',
|
||||
'conpherence-transaction-css',
|
||||
|
@@ -1,34 +1,4 @@
|
||||
<?php
|
||||
|
||||
// Rebuild all Conpherence Room images to profile standards
|
||||
//
|
||||
$table = new ConpherenceThread();
|
||||
$conn = $table->establishConnection('w');
|
||||
$table_name = 'conpherence_thread';
|
||||
|
||||
foreach (new LiskRawMigrationIterator($conn, $table_name) as $row) {
|
||||
|
||||
$images = phutil_json_decode($row['imagePHIDs']);
|
||||
if (!$images) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$file_phid = idx($images, 'original');
|
||||
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withPHIDs(array($file_phid))
|
||||
->executeOne();
|
||||
|
||||
$xform = PhabricatorFileTransform::getTransformByKey(
|
||||
PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE);
|
||||
$xformed = $xform->executeTransform($file);
|
||||
$new_phid = $xformed->getPHID();
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'UPDATE %T SET profileImagePHID = %s WHERE id = %d',
|
||||
$table->getTableName(),
|
||||
$new_phid,
|
||||
$row['id']);
|
||||
}
|
||||
// This migration once resized room images for Conpherence, but the File table
|
||||
// later changed significantly. See T12628.
|
||||
|
@@ -1,7 +1,15 @@
|
||||
<?php
|
||||
|
||||
$search_engine = PhabricatorFulltextStorageEngine::loadEngine();
|
||||
$use_mysql = ($search_engine instanceof PhabricatorMySQLFulltextStorageEngine);
|
||||
|
||||
$use_mysql = false;
|
||||
|
||||
$services = PhabricatorSearchService::getAllServices();
|
||||
foreach ($services as $service) {
|
||||
$engine = $service->getEngine();
|
||||
if ($engine instanceof PhabricatorMySQLFulltextStorageEngine) {
|
||||
$use_mysql = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($use_mysql) {
|
||||
$field = new PhabricatorSearchDocumentField();
|
||||
|
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
|
||||
ADD lastActionDiffPHID VARBINARY(64);
|
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
|
||||
ADD lastCommentDiffPHID VARBINARY(64);
|
125
resources/sql/autopatches/20170320.reviewers.03.migrate.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
$table = new DifferentialRevision();
|
||||
$diff_table = new DifferentialDiff();
|
||||
$reviewer_table = new DifferentialReviewer();
|
||||
|
||||
$table_name = PhabricatorEdgeConfig::TABLE_NAME_EDGE;
|
||||
$data_name = PhabricatorEdgeConfig::TABLE_NAME_EDGEDATA;
|
||||
|
||||
$conn = $table->establishConnection('w');
|
||||
|
||||
// Previously "DifferentialRevisionHasReviewerEdgeType::EDGECONST".
|
||||
$edge_type = 35;
|
||||
|
||||
// NOTE: We can't use normal migration iterators for edges because they don't
|
||||
// have an "id" column. For now, try just loading the whole result set: the
|
||||
// actual size of the rows is small. If we run into issues, we could write an
|
||||
// EdgeIterator.
|
||||
$every_edge = queryfx_all(
|
||||
$conn,
|
||||
'SELECT * FROM %T edge LEFT JOIN %T data ON edge.dataID = data.id
|
||||
WHERE edge.type = %d',
|
||||
$table_name,
|
||||
$data_name,
|
||||
$edge_type);
|
||||
|
||||
foreach ($every_edge as $edge) {
|
||||
if ($edge['type'] != $edge_type) {
|
||||
// Ignore edges which aren't "reviewers", like subscribers.
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = phutil_json_decode($edge['data']);
|
||||
$data = idx($data, 'data');
|
||||
} catch (Exception $ex) {
|
||||
// Just ignore any kind of issue with the edge data, we'll use a default
|
||||
// below.
|
||||
$data = null;
|
||||
}
|
||||
|
||||
if (!$data) {
|
||||
$data = array(
|
||||
'status' => 'added',
|
||||
);
|
||||
}
|
||||
|
||||
$status = idx($data, 'status');
|
||||
|
||||
$diff_phid = null;
|
||||
|
||||
// NOTE: At one point, the code to populate "diffID" worked correctly, but
|
||||
// it seems to have later been broken. Salvage it if we can, and look up
|
||||
// the corresponding diff PHID.
|
||||
$diff_id = idx($data, 'diffID');
|
||||
if ($diff_id) {
|
||||
$row = queryfx_one(
|
||||
$conn,
|
||||
'SELECT phid FROM %T WHERE id = %d',
|
||||
$diff_table->getTableName(),
|
||||
$diff_id);
|
||||
if ($row) {
|
||||
$diff_phid = $row['phid'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$diff_phid) {
|
||||
// If the status is "accepted" or "rejected", look up the current diff
|
||||
// PHID so we can distinguish between "accepted" and "accepted older".
|
||||
switch ($status) {
|
||||
case 'accepted':
|
||||
case 'rejected':
|
||||
case 'commented':
|
||||
$row = queryfx_one(
|
||||
$conn,
|
||||
'SELECT diff.phid FROM %T diff JOIN %T revision
|
||||
ON diff.revisionID = revision.id
|
||||
WHERE revision.phid = %s
|
||||
ORDER BY diff.id DESC LIMIT 1',
|
||||
$diff_table->getTableName(),
|
||||
$table->getTableName(),
|
||||
$edge['src']);
|
||||
if ($row) {
|
||||
$diff_phid = $row['phid'];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We now represent some states (like "Commented" and "Accepted Older") as
|
||||
// a primary state plus an extra flag, instead of making "Commented" a
|
||||
// primary state. Map old states to new states and flags.
|
||||
|
||||
if ($status == 'commented') {
|
||||
$status = 'added';
|
||||
$comment_phid = $diff_phid;
|
||||
$action_phid = null;
|
||||
} else {
|
||||
$comment_phid = null;
|
||||
$action_phid = $diff_phid;
|
||||
}
|
||||
|
||||
if ($status == 'accepted-older') {
|
||||
$status = 'accepted';
|
||||
}
|
||||
|
||||
if ($status == 'rejected-older') {
|
||||
$status = 'rejected';
|
||||
}
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'INSERT INTO %T (revisionPHID, reviewerPHID, reviewerStatus,
|
||||
lastActionDiffPHID, lastCommentDiffPHID, dateCreated, dateModified)
|
||||
VALUES (%s, %s, %s, %ns, %ns, %d, %d)
|
||||
ON DUPLICATE KEY UPDATE dateCreated = VALUES(dateCreated)',
|
||||
$reviewer_table->getTableName(),
|
||||
$edge['src'],
|
||||
$edge['dst'],
|
||||
$status,
|
||||
$action_phid,
|
||||
$comment_phid,
|
||||
$edge['dateCreated'],
|
||||
$edge['dateCreated']);
|
||||
}
|
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
|
||||
ADD lastActorPHID VARBINARY(64);
|
2
resources/sql/autopatches/20170328.reviewers.01.void.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
|
||||
ADD voidedPHID VARBINARY(64);
|
8
resources/sql/autopatches/20170406.hmac.01.keystore.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE {$NAMESPACE}_auth.auth_hmackey (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
keyName VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
keyValue VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_name` (keyName)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
42
resources/sql/autopatches/20170410.calendar.01.repair.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
// See T12488. Some events survived "20161004.cal.01.noepoch.php" without
|
||||
// having "utcInstanceEpoch" computed, which breaks ICS export. This appears
|
||||
// to be the result of some bug which has been fixed in the meantime, so just
|
||||
// redo this part of the migration.
|
||||
|
||||
$table = new PhabricatorCalendarEvent();
|
||||
$conn = $table->establishConnection('w');
|
||||
$table_name = $table->getTableName();
|
||||
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
$all_events = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->execute();
|
||||
foreach ($all_events as $event) {
|
||||
$id = $event->getID();
|
||||
|
||||
if (!$event->getInstanceOfEventPHID()) {
|
||||
// Not a child event, so no instance epoch.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($event->getUTCInstanceEpoch()) {
|
||||
// Already has an instance epoch.
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$event->updateUTCEpochs();
|
||||
} catch (Exception $ex) {
|
||||
phlog($ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'UPDATE %T SET utcInstanceEpoch = %nd WHERE id = %d',
|
||||
$table_name,
|
||||
$event->getUTCInstanceEpoch(),
|
||||
$id);
|
||||
}
|
@@ -0,0 +1,2 @@
|
||||
DELETE FROM {$NAMESPACE}_conpherence.conpherence_transaction
|
||||
WHERE transactionType = 'picture-crop';
|
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_thread
|
||||
DROP COLUMN recentParticipantPHIDs;
|
7
resources/sql/autopatches/20170417.files.ngrams.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE {$NAMESPACE}_file.file_filename_ngrams (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
objectID INT UNSIGNED NOT NULL,
|
||||
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
KEY `key_object` (objectID),
|
||||
KEY `key_ngram` (ngram, objectID)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE {$NAMESPACE}_application.application_applicationtransaction (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
authorPHID VARBINARY(64) NOT NULL,
|
||||
objectPHID VARBINARY(64) NOT NULL,
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
commentPHID VARBINARY(64) DEFAULT NULL,
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (`phid`),
|
||||
KEY `key_object` (`objectPHID`)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
16
resources/sql/autopatches/20170418.1.application.02.edge.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE {$NAMESPACE}_application.edge (
|
||||
src VARBINARY(64) NOT NULL,
|
||||
type INT UNSIGNED NOT NULL,
|
||||
dst VARBINARY(64) NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
seq INT UNSIGNED NOT NULL,
|
||||
dataID INT UNSIGNED,
|
||||
PRIMARY KEY (src, type, dst),
|
||||
KEY `src` (src, type, dateCreated, seq),
|
||||
UNIQUE KEY `key_dst` (dst, type, src)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
||||
|
||||
CREATE TABLE {$NAMESPACE}_application.edgedata (
|
||||
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20170418.files.isDeleted.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_file.file
|
||||
ADD isDeleted BOOL NOT NULL DEFAULT 0;
|
7
resources/sql/autopatches/20170419.app.01.table.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE {$NAMESPACE}_application.application_application (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (phid)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20170419.thread.01.behind.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant
|
||||
DROP behindTransactionPHID;
|
2
resources/sql/autopatches/20170419.thread.02.status.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant
|
||||
DROP participationStatus;
|
2
resources/sql/autopatches/20170419.thread.03.touched.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant
|
||||
DROP dateTouched;
|
34
resources/sql/autopatches/20170424.user.01.verify.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
$table = new PhabricatorUser();
|
||||
$conn = $table->establishConnection('w');
|
||||
|
||||
foreach (new LiskMigrationIterator($table) as $user) {
|
||||
// Ignore users who are verified.
|
||||
if ($user->getIsEmailVerified()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore unverified users with missing (rare) or unverified (common)
|
||||
// primary emails: it's correct that their accounts are not verified.
|
||||
$primary = $user->loadPrimaryEmail();
|
||||
if (!$primary) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$primary->getIsVerified()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'UPDATE %T SET isEmailVerified = 1 WHERE id = %d',
|
||||
$table->getTableName(),
|
||||
$user->getID());
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Corrected account verification state for user "%s".',
|
||||
$user->getUsername()));
|
||||
}
|
@@ -14,7 +14,7 @@ foreach ($sessions as $session) {
|
||||
$conn,
|
||||
'UPDATE %T SET sessionKey = %s WHERE userPHID = %s AND type = %s',
|
||||
PhabricatorUser::SESSION_TABLE,
|
||||
PhabricatorHash::digest($session['sessionKey']),
|
||||
PhabricatorHash::weakDigest($session['sessionKey']),
|
||||
$session['userPHID'],
|
||||
$session['type']);
|
||||
}
|
||||
|
543
resources/sql/stopwords_myisam.txt
Normal file
@@ -0,0 +1,543 @@
|
||||
a's
|
||||
able
|
||||
about
|
||||
above
|
||||
according
|
||||
accordingly
|
||||
across
|
||||
actually
|
||||
after
|
||||
afterwards
|
||||
again
|
||||
against
|
||||
ain't
|
||||
all
|
||||
allow
|
||||
allows
|
||||
almost
|
||||
alone
|
||||
along
|
||||
already
|
||||
also
|
||||
although
|
||||
always
|
||||
am
|
||||
among
|
||||
amongst
|
||||
an
|
||||
and
|
||||
another
|
||||
any
|
||||
anybody
|
||||
anyhow
|
||||
anyone
|
||||
anything
|
||||
anyway
|
||||
anyways
|
||||
anywhere
|
||||
apart
|
||||
appear
|
||||
appreciate
|
||||
appropriate
|
||||
are
|
||||
aren't
|
||||
around
|
||||
as
|
||||
aside
|
||||
ask
|
||||
asking
|
||||
associated
|
||||
at
|
||||
available
|
||||
away
|
||||
awfully
|
||||
be
|
||||
became
|
||||
because
|
||||
become
|
||||
becomes
|
||||
becoming
|
||||
been
|
||||
before
|
||||
beforehand
|
||||
behind
|
||||
being
|
||||
believe
|
||||
below
|
||||
beside
|
||||
besides
|
||||
best
|
||||
better
|
||||
between
|
||||
beyond
|
||||
both
|
||||
brief
|
||||
but
|
||||
by
|
||||
c'mon
|
||||
c's
|
||||
came
|
||||
can
|
||||
can't
|
||||
cannot
|
||||
cant
|
||||
cause
|
||||
causes
|
||||
certain
|
||||
certainly
|
||||
changes
|
||||
clearly
|
||||
co
|
||||
com
|
||||
come
|
||||
comes
|
||||
concerning
|
||||
consequently
|
||||
consider
|
||||
considering
|
||||
contain
|
||||
containing
|
||||
contains
|
||||
corresponding
|
||||
could
|
||||
couldn't
|
||||
course
|
||||
currently
|
||||
definitely
|
||||
described
|
||||
despite
|
||||
did
|
||||
didn't
|
||||
different
|
||||
do
|
||||
does
|
||||
doesn't
|
||||
doing
|
||||
don't
|
||||
done
|
||||
down
|
||||
downwards
|
||||
during
|
||||
each
|
||||
edu
|
||||
eg
|
||||
eight
|
||||
either
|
||||
else
|
||||
elsewhere
|
||||
enough
|
||||
entirely
|
||||
especially
|
||||
et
|
||||
etc
|
||||
even
|
||||
ever
|
||||
every
|
||||
everybody
|
||||
everyone
|
||||
everything
|
||||
everywhere
|
||||
ex
|
||||
exactly
|
||||
example
|
||||
except
|
||||
far
|
||||
few
|
||||
fifth
|
||||
first
|
||||
five
|
||||
followed
|
||||
following
|
||||
follows
|
||||
for
|
||||
former
|
||||
formerly
|
||||
forth
|
||||
four
|
||||
from
|
||||
further
|
||||
furthermore
|
||||
get
|
||||
gets
|
||||
getting
|
||||
given
|
||||
gives
|
||||
go
|
||||
goes
|
||||
going
|
||||
gone
|
||||
got
|
||||
gotten
|
||||
greetings
|
||||
had
|
||||
hadn't
|
||||
happens
|
||||
hardly
|
||||
has
|
||||
hasn't
|
||||
have
|
||||
haven't
|
||||
having
|
||||
he
|
||||
he's
|
||||
hello
|
||||
help
|
||||
hence
|
||||
her
|
||||
here
|
||||
here's
|
||||
hereafter
|
||||
hereby
|
||||
herein
|
||||
hereupon
|
||||
hers
|
||||
herself
|
||||
hi
|
||||
him
|
||||
himself
|
||||
his
|
||||
hither
|
||||
hopefully
|
||||
how
|
||||
howbeit
|
||||
however
|
||||
i'd
|
||||
i'll
|
||||
i'm
|
||||
i've
|
||||
ie
|
||||
if
|
||||
ignored
|
||||
immediate
|
||||
in
|
||||
inasmuch
|
||||
inc
|
||||
indeed
|
||||
indicate
|
||||
indicated
|
||||
indicates
|
||||
inner
|
||||
insofar
|
||||
instead
|
||||
into
|
||||
inward
|
||||
is
|
||||
isn't
|
||||
it
|
||||
it'd
|
||||
it'll
|
||||
it's
|
||||
its
|
||||
itself
|
||||
just
|
||||
keep
|
||||
keeps
|
||||
kept
|
||||
know
|
||||
known
|
||||
knows
|
||||
last
|
||||
lately
|
||||
later
|
||||
latter
|
||||
latterly
|
||||
least
|
||||
less
|
||||
lest
|
||||
let
|
||||
let's
|
||||
like
|
||||
liked
|
||||
likely
|
||||
little
|
||||
look
|
||||
looking
|
||||
looks
|
||||
ltd
|
||||
mainly
|
||||
many
|
||||
may
|
||||
maybe
|
||||
me
|
||||
mean
|
||||
meanwhile
|
||||
merely
|
||||
might
|
||||
more
|
||||
moreover
|
||||
most
|
||||
mostly
|
||||
much
|
||||
must
|
||||
my
|
||||
myself
|
||||
name
|
||||
namely
|
||||
nd
|
||||
near
|
||||
nearly
|
||||
necessary
|
||||
need
|
||||
needs
|
||||
neither
|
||||
never
|
||||
nevertheless
|
||||
new
|
||||
next
|
||||
nine
|
||||
no
|
||||
nobody
|
||||
non
|
||||
none
|
||||
noone
|
||||
nor
|
||||
normally
|
||||
not
|
||||
nothing
|
||||
novel
|
||||
now
|
||||
nowhere
|
||||
obviously
|
||||
of
|
||||
off
|
||||
often
|
||||
oh
|
||||
ok
|
||||
okay
|
||||
old
|
||||
on
|
||||
once
|
||||
one
|
||||
ones
|
||||
only
|
||||
onto
|
||||
or
|
||||
other
|
||||
others
|
||||
otherwise
|
||||
ought
|
||||
our
|
||||
ours
|
||||
ourselves
|
||||
out
|
||||
outside
|
||||
over
|
||||
overall
|
||||
own
|
||||
particular
|
||||
particularly
|
||||
per
|
||||
perhaps
|
||||
placed
|
||||
please
|
||||
plus
|
||||
possible
|
||||
presumably
|
||||
probably
|
||||
provides
|
||||
que
|
||||
quite
|
||||
qv
|
||||
rather
|
||||
rd
|
||||
re
|
||||
really
|
||||
reasonably
|
||||
regarding
|
||||
regardless
|
||||
regards
|
||||
relatively
|
||||
respectively
|
||||
right
|
||||
said
|
||||
same
|
||||
saw
|
||||
say
|
||||
saying
|
||||
says
|
||||
second
|
||||
secondly
|
||||
see
|
||||
seeing
|
||||
seem
|
||||
seemed
|
||||
seeming
|
||||
seems
|
||||
seen
|
||||
self
|
||||
selves
|
||||
sensible
|
||||
sent
|
||||
serious
|
||||
seriously
|
||||
seven
|
||||
several
|
||||
shall
|
||||
she
|
||||
should
|
||||
shouldn't
|
||||
since
|
||||
six
|
||||
so
|
||||
some
|
||||
somebody
|
||||
somehow
|
||||
someone
|
||||
something
|
||||
sometime
|
||||
sometimes
|
||||
somewhat
|
||||
somewhere
|
||||
soon
|
||||
sorry
|
||||
specified
|
||||
specify
|
||||
specifying
|
||||
still
|
||||
sub
|
||||
such
|
||||
sup
|
||||
sure
|
||||
t's
|
||||
take
|
||||
taken
|
||||
tell
|
||||
tends
|
||||
th
|
||||
than
|
||||
thank
|
||||
thanks
|
||||
thanx
|
||||
that
|
||||
that's
|
||||
thats
|
||||
the
|
||||
their
|
||||
theirs
|
||||
them
|
||||
themselves
|
||||
then
|
||||
thence
|
||||
there
|
||||
there's
|
||||
thereafter
|
||||
thereby
|
||||
therefore
|
||||
therein
|
||||
theres
|
||||
thereupon
|
||||
these
|
||||
they
|
||||
they'd
|
||||
they'll
|
||||
they're
|
||||
they've
|
||||
think
|
||||
third
|
||||
this
|
||||
thorough
|
||||
thoroughly
|
||||
those
|
||||
though
|
||||
three
|
||||
through
|
||||
throughout
|
||||
thru
|
||||
thus
|
||||
to
|
||||
together
|
||||
too
|
||||
took
|
||||
toward
|
||||
towards
|
||||
tried
|
||||
tries
|
||||
truly
|
||||
try
|
||||
trying
|
||||
twice
|
||||
two
|
||||
un
|
||||
under
|
||||
unfortunately
|
||||
unless
|
||||
unlikely
|
||||
until
|
||||
unto
|
||||
up
|
||||
upon
|
||||
us
|
||||
use
|
||||
used
|
||||
useful
|
||||
uses
|
||||
using
|
||||
usually
|
||||
value
|
||||
various
|
||||
very
|
||||
via
|
||||
viz
|
||||
vs
|
||||
want
|
||||
wants
|
||||
was
|
||||
wasn't
|
||||
way
|
||||
we
|
||||
we'd
|
||||
we'll
|
||||
we're
|
||||
we've
|
||||
welcome
|
||||
well
|
||||
went
|
||||
were
|
||||
weren't
|
||||
what
|
||||
what's
|
||||
whatever
|
||||
when
|
||||
whence
|
||||
whenever
|
||||
where
|
||||
where's
|
||||
whereafter
|
||||
whereas
|
||||
whereby
|
||||
wherein
|
||||
whereupon
|
||||
wherever
|
||||
whether
|
||||
which
|
||||
while
|
||||
whither
|
||||
who
|
||||
who's
|
||||
whoever
|
||||
whole
|
||||
whom
|
||||
whose
|
||||
why
|
||||
will
|
||||
willing
|
||||
wish
|
||||
with
|
||||
within
|
||||
without
|
||||
won't
|
||||
wonder
|
||||
would
|
||||
wouldn't
|
||||
yes
|
||||
yet
|
||||
you
|
||||
you'd
|
||||
you'll
|
||||
you're
|
||||
you've
|
||||
your
|
||||
yours
|
||||
yourself
|
||||
yourselves
|
||||
zero
|
@@ -205,9 +205,8 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
||||
DarkConsoleXHProfPluginAPI::saveProfilerSample($access_log);
|
||||
|
||||
// Add points to the rate limits for this request.
|
||||
if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
$user_ip = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
$rate_token = PhabricatorStartup::getRateLimitToken();
|
||||
if ($rate_token !== null) {
|
||||
// The base score for a request allows users to make 30 requests per
|
||||
// minute.
|
||||
$score = (1000 / 30);
|
||||
@@ -217,7 +216,7 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
||||
$score = $score / 5;
|
||||
}
|
||||
|
||||
PhabricatorStartup::addRateLimitScore($user_ip, $score);
|
||||
PhabricatorStartup::addRateLimitScore($rate_token, $score);
|
||||
}
|
||||
|
||||
if ($processing_exception) {
|
||||
|
@@ -94,10 +94,15 @@ final class AphrontFileResponse extends AphrontResponse {
|
||||
array('Accept-Ranges', 'bytes'),
|
||||
);
|
||||
|
||||
if ($this->rangeMin || $this->rangeMax) {
|
||||
if ($this->rangeMin !== null || $this->rangeMax !== null) {
|
||||
$len = $this->getContentLength();
|
||||
$min = $this->rangeMin;
|
||||
|
||||
$max = $this->rangeMax;
|
||||
if ($max === null) {
|
||||
$max = ($len - 1);
|
||||
}
|
||||
|
||||
$headers[] = array('Content-Range', "bytes {$min}-{$max}/{$len}");
|
||||
$content_len = ($max - $min) + 1;
|
||||
} else {
|
||||
@@ -105,7 +110,7 @@ final class AphrontFileResponse extends AphrontResponse {
|
||||
}
|
||||
|
||||
if (!$this->shouldCompressResponse()) {
|
||||
$headers[] = array('Content-Length', $this->getContentLength());
|
||||
$headers[] = array('Content-Length', $content_len);
|
||||
}
|
||||
|
||||
if (strlen($this->getDownload())) {
|
||||
@@ -134,4 +139,29 @@ final class AphrontFileResponse extends AphrontResponse {
|
||||
return $this->getCompressResponse();
|
||||
}
|
||||
|
||||
public function parseHTTPRange($range) {
|
||||
$begin = null;
|
||||
$end = null;
|
||||
|
||||
$matches = null;
|
||||
if (preg_match('/^bytes=(\d+)-(\d*)$/', $range, $matches)) {
|
||||
// Note that the "Range" header specifies bytes differently than
|
||||
// we do internally: the range 0-1 has 2 bytes (byte 0 and byte 1).
|
||||
$begin = (int)$matches[1];
|
||||
|
||||
// The "Range" may be "200-299" or "200-", meaning "until end of file".
|
||||
if (strlen($matches[2])) {
|
||||
$range_end = (int)$matches[2];
|
||||
$end = $range_end + 1;
|
||||
} else {
|
||||
$range_end = null;
|
||||
}
|
||||
|
||||
$this->setHTTPResponseCode(206);
|
||||
$this->setRange($begin, $range_end);
|
||||
}
|
||||
|
||||
return array($begin, $end);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -178,10 +178,13 @@ final class PhabricatorCommitSearchEngine
|
||||
$groups = $bucket->newResultGroups($query, $commits);
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$views[] = id(clone $template)
|
||||
->setHeader($group->getName())
|
||||
->setNoDataString($group->getNoDataString())
|
||||
->setCommits($group->getObjects());
|
||||
// Don't show groups in Dashboard Panels
|
||||
if ($group->getObjects() || !$this->isPanelContext()) {
|
||||
$views[] = id(clone $template)
|
||||
->setHeader($group->getName())
|
||||
->setNoDataString($group->getNoDataString())
|
||||
->setCommits($group->getObjects());
|
||||
}
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$this->addError($ex->getMessage());
|
||||
@@ -189,7 +192,13 @@ final class PhabricatorCommitSearchEngine
|
||||
} else {
|
||||
$views[] = id(clone $template)
|
||||
->setCommits($commits)
|
||||
->setNoDataString(pht('No matching commits.'));
|
||||
->setNoDataString(pht('No commits found.'));
|
||||
}
|
||||
|
||||
if (!$views) {
|
||||
$views[] = id(new PhabricatorAuditListView())
|
||||
->setViewer($viewer)
|
||||
->setNoDataString(pht('No commits found.'));
|
||||
}
|
||||
|
||||
if (count($views) == 1) {
|
||||
|
@@ -194,7 +194,7 @@ abstract class PhabricatorAuthController extends PhabricatorController {
|
||||
// hijacking registration sessions.
|
||||
|
||||
$actual = $account->getProperty('registrationKey');
|
||||
$expect = PhabricatorHash::digest($registration_key);
|
||||
$expect = PhabricatorHash::weakDigest($registration_key);
|
||||
if (!phutil_hashes_are_identical($actual, $expect)) {
|
||||
$response = $this->renderError(
|
||||
pht(
|
||||
|
@@ -194,7 +194,7 @@ final class PhabricatorAuthLoginController
|
||||
$registration_key = Filesystem::readRandomCharacters(32);
|
||||
$account->setProperty(
|
||||
'registrationKey',
|
||||
PhabricatorHash::digest($registration_key));
|
||||
PhabricatorHash::weakDigest($registration_key));
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$account->save();
|
||||
|
@@ -135,7 +135,7 @@ final class PhabricatorAuthOneTimeLoginController
|
||||
->setTokenResource($target_user->getPHID())
|
||||
->setTokenType($password_type)
|
||||
->setTokenExpires(time() + phutil_units('1 hour in seconds'))
|
||||
->setTokenCode(PhabricatorHash::digest($key))
|
||||
->setTokenCode(PhabricatorHash::weakDigest($key))
|
||||
->save();
|
||||
unset($unguarded);
|
||||
|
||||
|
@@ -24,11 +24,11 @@ final class PhabricatorAuthSSHKeyGenerateController
|
||||
$keys = PhabricatorSSHKeyGenerator::generateKeypair();
|
||||
list($public_key, $private_key) = $keys;
|
||||
|
||||
$file = PhabricatorFile::buildFromFileDataOrHash(
|
||||
$file = PhabricatorFile::newFromFileData(
|
||||
$private_key,
|
||||
array(
|
||||
'name' => $default_name.'.key',
|
||||
'ttl' => time() + (60 * 10),
|
||||
'ttl.relative' => phutil_units('10 minutes in seconds'),
|
||||
'viewPolicy' => $viewer->getPHID(),
|
||||
));
|
||||
|
||||
|
@@ -16,7 +16,7 @@ final class PhabricatorAuthTerminateSessionController
|
||||
$query->withIDs(array($id));
|
||||
}
|
||||
|
||||
$current_key = PhabricatorHash::digest(
|
||||
$current_key = PhabricatorHash::weakDigest(
|
||||
$request->getCookie(PhabricatorCookies::COOKIE_SESSION));
|
||||
|
||||
$sessions = $query->execute();
|
||||
|
@@ -197,9 +197,7 @@ final class PhabricatorAuthSSHKeyEditor
|
||||
|
||||
// After making any change to an SSH key, drop the authfile cache so it
|
||||
// is regenerated the next time anyone authenticates.
|
||||
$cache = PhabricatorCaches::getMutableCache();
|
||||
$authfile_key = PhabricatorAuthSSHKeyQuery::AUTHFILE_CACHEKEY;
|
||||
$cache->deleteKey($authfile_key);
|
||||
PhabricatorAuthSSHKeyQuery::deleteSSHKeyCache();
|
||||
|
||||
return $xactions;
|
||||
}
|
||||
|
@@ -110,7 +110,7 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
$session_table = new PhabricatorAuthSession();
|
||||
$user_table = new PhabricatorUser();
|
||||
$conn_r = $session_table->establishConnection('r');
|
||||
$session_key = PhabricatorHash::digest($session_token);
|
||||
$session_key = PhabricatorHash::weakDigest($session_token);
|
||||
|
||||
$cache_parts = $this->getUserCacheQueryParts($conn_r);
|
||||
list($cache_selects, $cache_joins, $cache_map, $types_map) = $cache_parts;
|
||||
@@ -240,7 +240,7 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
// This has a side effect of validating the session type.
|
||||
$session_ttl = PhabricatorAuthSession::getSessionTypeTTL($session_type);
|
||||
|
||||
$digest_key = PhabricatorHash::digest($session_key);
|
||||
$digest_key = PhabricatorHash::weakDigest($session_key);
|
||||
|
||||
// Logging-in users don't have CSRF stuff yet, so we have to unguard this
|
||||
// write.
|
||||
@@ -306,7 +306,7 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
->execute();
|
||||
|
||||
if ($except_session !== null) {
|
||||
$except_session = PhabricatorHash::digest($except_session);
|
||||
$except_session = PhabricatorHash::weakDigest($except_session);
|
||||
}
|
||||
|
||||
foreach ($sessions as $key => $session) {
|
||||
@@ -755,7 +755,7 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
$parts[] = $email->getVerificationCode();
|
||||
}
|
||||
|
||||
return PhabricatorHash::digest(implode(':', $parts));
|
||||
return PhabricatorHash::weakDigest(implode(':', $parts));
|
||||
}
|
||||
|
||||
|
||||
|
@@ -39,7 +39,7 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
||||
->withTokenResources(array($user->getPHID()))
|
||||
->withTokenTypes(array($totp_token_type))
|
||||
->withExpired(false)
|
||||
->withTokenCodes(array(PhabricatorHash::digest($key)))
|
||||
->withTokenCodes(array(PhabricatorHash::weakDigest($key)))
|
||||
->executeOne();
|
||||
if (!$temporary_token) {
|
||||
// If we don't have a matching token, regenerate the key below.
|
||||
@@ -58,7 +58,7 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
||||
->setTokenResource($user->getPHID())
|
||||
->setTokenType($totp_token_type)
|
||||
->setTokenExpires(time() + phutil_units('1 hour in seconds'))
|
||||
->setTokenCode(PhabricatorHash::digest($key))
|
||||
->setTokenCode(PhabricatorHash::weakDigest($key))
|
||||
->save();
|
||||
unset($unguarded);
|
||||
}
|
||||
|
@@ -474,7 +474,7 @@ abstract class PhabricatorAuthProvider extends Phobject {
|
||||
true);
|
||||
}
|
||||
|
||||
return PhabricatorHash::digest($phcid);
|
||||
return PhabricatorHash::weakDigest($phcid);
|
||||
}
|
||||
|
||||
protected function verifyAuthCSRFCode(AphrontRequest $request, $actual) {
|
||||
|
@@ -11,6 +11,12 @@ final class PhabricatorAuthSSHKeyQuery
|
||||
private $keys;
|
||||
private $isActive;
|
||||
|
||||
public static function deleteSSHKeyCache() {
|
||||
$cache = PhabricatorCaches::getMutableCache();
|
||||
$authfile_key = self::AUTHFILE_CACHEKEY;
|
||||
$cache->deleteKey($authfile_key);
|
||||
}
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
|
@@ -85,7 +85,7 @@ final class PhabricatorAuthSessionQuery
|
||||
if ($this->sessionKeys) {
|
||||
$hashes = array();
|
||||
foreach ($this->sessionKeys as $session_key) {
|
||||
$hashes[] = PhabricatorHash::digest($session_key);
|
||||
$hashes[] = PhabricatorHash::weakDigest($session_key);
|
||||
}
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
|
24
src/applications/auth/storage/PhabricatorAuthHMACKey.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthHMACKey
|
||||
extends PhabricatorAuthDAO {
|
||||
|
||||
protected $keyName;
|
||||
protected $keyValue;
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'keyName' => 'text64',
|
||||
'keyValue' => 'text128',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_name' => array(
|
||||
'columns' => array('keyName'),
|
||||
'unique' => true,
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
}
|
@@ -11,7 +11,7 @@ final class PhabricatorBadgesApplication extends PhabricatorApplication {
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
return pht('Achievements and Notority');
|
||||
return pht('Achievements and Notoriety');
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
@@ -26,10 +26,6 @@ final class PhabricatorBadgesApplication extends PhabricatorApplication {
|
||||
return self::GROUP_UTILITIES;
|
||||
}
|
||||
|
||||
public function isPrototype() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/badges/' => array(
|
||||
|
@@ -1,103 +0,0 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorBadgeHasRecipientEdgeType
|
||||
extends PhabricatorEdgeType {
|
||||
|
||||
const EDGECONST = 59;
|
||||
|
||||
public function getInverseEdgeConstant() {
|
||||
return PhabricatorRecipientHasBadgeEdgeType::EDGECONST;
|
||||
}
|
||||
|
||||
public function shouldWriteInverseTransactions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getTransactionAddString(
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s awarded %s recipients(s): %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges);
|
||||
}
|
||||
|
||||
public function getTransactionRemoveString(
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s revoked %s recipients(s): %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
public function getTransactionEditString(
|
||||
$actor,
|
||||
$total_count,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited recipient(s), awarded %s: %s; revoked %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
public function getFeedAddString(
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s awarded %s recipient(s) for %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$object,
|
||||
$add_edges);
|
||||
}
|
||||
|
||||
public function getFeedRemoveString(
|
||||
$actor,
|
||||
$object,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s revoked %s recipient(s) for %s: %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$object,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
public function getFeedEditString(
|
||||
$actor,
|
||||
$object,
|
||||
$total_count,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited recipient(s) for %s, awarded %s: %s; revoked %s: %s.',
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
}
|
||||
}
|
@@ -1,103 +0,0 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorRecipientHasBadgeEdgeType
|
||||
extends PhabricatorEdgeType {
|
||||
|
||||
const EDGECONST = 58;
|
||||
|
||||
public function getInverseEdgeConstant() {
|
||||
return PhabricatorBadgeHasRecipientEdgeType::EDGECONST;
|
||||
}
|
||||
|
||||
public function shouldWriteInverseTransactions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getTransactionAddString(
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s badge(s): %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges);
|
||||
}
|
||||
|
||||
public function getTransactionRemoveString(
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s revoked %s badge(s): %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
public function getTransactionEditString(
|
||||
$actor,
|
||||
$total_count,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited badge(s), added %s: %s; revoked %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
public function getFeedAddString(
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s badge(s) for %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$object,
|
||||
$add_edges);
|
||||
}
|
||||
|
||||
public function getFeedRemoveString(
|
||||
$actor,
|
||||
$object,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s revoked %s badge(s) for %s: %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$object,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
public function getFeedEditString(
|
||||
$actor,
|
||||
$object,
|
||||
$total_count,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited badge(s) for %s, added %s: %s; revoked %s: %s.',
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
}
|
||||
}
|
@@ -43,7 +43,7 @@ final class PhabricatorBadgesBadgeNameTransaction
|
||||
$new_value = $xaction->getNewValue();
|
||||
$new_length = strlen($new_value);
|
||||
if ($new_length > $max_length) {
|
||||
$errors[] = $this->newRequiredError(
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht('The name can be no longer than %s characters.',
|
||||
new PhutilNumber($max_length)));
|
||||
}
|
||||
|
@@ -9,8 +9,10 @@
|
||||
* @task meta Application Management
|
||||
*/
|
||||
abstract class PhabricatorApplication
|
||||
extends Phobject
|
||||
implements PhabricatorPolicyInterface {
|
||||
extends PhabricatorLiskDAO
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorApplicationTransactionInterface {
|
||||
|
||||
const GROUP_CORE = 'core';
|
||||
const GROUP_UTILITIES = 'util';
|
||||
@@ -26,6 +28,30 @@ abstract class PhabricatorApplication
|
||||
);
|
||||
}
|
||||
|
||||
final public function getApplicationName() {
|
||||
return 'application';
|
||||
}
|
||||
|
||||
final public function getTableName() {
|
||||
return 'application_application';
|
||||
}
|
||||
|
||||
final protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
final public function generatePHID() {
|
||||
return $this->getPHID();
|
||||
}
|
||||
|
||||
final public function save() {
|
||||
// When "save()" is called on applications, we just return without
|
||||
// actually writing anything to the database.
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/* -( Application Information )-------------------------------------------- */
|
||||
|
||||
@@ -613,4 +639,25 @@ abstract class PhabricatorApplication
|
||||
);
|
||||
}
|
||||
|
||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||
|
||||
|
||||
public function getApplicationTransactionEditor() {
|
||||
return new PhutilMethodNotImplementedException(pht('Coming Soon!'));
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorApplicationApplicationTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
}
|
||||
|
@@ -98,7 +98,7 @@ abstract class PhabricatorController extends AphrontController {
|
||||
|
||||
|
||||
if (!$user->isLoggedIn()) {
|
||||
$user->attachAlternateCSRFString(PhabricatorHash::digest($phsid));
|
||||
$user->attachAlternateCSRFString(PhabricatorHash::weakDigest($phsid));
|
||||
}
|
||||
|
||||
$request->setUser($user);
|
||||
|
@@ -58,4 +58,12 @@ final class PhabricatorCalendarEventNotificationView
|
||||
return phabricator_datetime($epoch, $viewer);
|
||||
}
|
||||
|
||||
public function getDisplayTimeWithTimezone() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$epoch = $this->getEpoch();
|
||||
return phabricator_datetimezone($epoch, $viewer);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -268,7 +268,7 @@ final class PhabricatorCalendarNotificationEngine
|
||||
'%s is starting in %s minute(s), at %s.',
|
||||
$event->getEvent()->getName(),
|
||||
$event->getDisplayMinutes(),
|
||||
$event->getDisplayTime()));
|
||||
$event->getDisplayTimeWithTimezone()));
|
||||
|
||||
$body->addLinkSection(
|
||||
pht('EVENT DETAIL'),
|
||||
|
@@ -226,7 +226,7 @@ final class PhabricatorCalendarEventQuery
|
||||
$set = $event->newRecurrenceSet();
|
||||
|
||||
$recurrences = $set->getEventsBetween(
|
||||
null,
|
||||
$start_date,
|
||||
$end_date,
|
||||
$limit + 1);
|
||||
|
||||
|
@@ -391,6 +391,10 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||
return ($epoch - $window);
|
||||
}
|
||||
|
||||
public function getEndDateTimeEpochForCache() {
|
||||
return $this->getEndDateTimeEpoch();
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
@@ -1182,9 +1186,8 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||
* @task markup
|
||||
*/
|
||||
public function getMarkupFieldKey($field) {
|
||||
$hash = PhabricatorHash::digest($this->getMarkupText($field));
|
||||
$id = $this->getID();
|
||||
return "calendar:T{$id}:{$field}:{$hash}";
|
||||
$content = $this->getMarkupText($field);
|
||||
return PhabricatorMarkupEngine::digestRemarkupContent($this, $content);
|
||||
}
|
||||
|
||||
|
||||
@@ -1343,7 +1346,21 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||
PhabricatorDestructionEngine $engine) {
|
||||
|
||||
$this->openTransaction();
|
||||
$this->delete();
|
||||
$invitees = id(new PhabricatorCalendarEventInvitee())->loadAllWhere(
|
||||
'eventPHID = %s',
|
||||
$this->getPHID());
|
||||
foreach ($invitees as $invitee) {
|
||||
$invitee->delete();
|
||||
}
|
||||
|
||||
$notifications = id(new PhabricatorCalendarNotification())->loadAllWhere(
|
||||
'eventPHID = %s',
|
||||
$this->getPHID());
|
||||
foreach ($notifications as $notification) {
|
||||
$notification->delete();
|
||||
}
|
||||
|
||||
$this->delete();
|
||||
$this->saveTransaction();
|
||||
}
|
||||
|
||||
|
@@ -104,9 +104,30 @@ abstract class CelerityResourceController extends PhabricatorController {
|
||||
}
|
||||
|
||||
$response = id(new AphrontFileResponse())
|
||||
->setContent($data)
|
||||
->setMimeType($type_map[$type])
|
||||
->setCompressResponse(true);
|
||||
->setMimeType($type_map[$type]);
|
||||
|
||||
$range = AphrontRequest::getHTTPHeader('Range');
|
||||
|
||||
if (strlen($range)) {
|
||||
$response->setContentLength(strlen($data));
|
||||
|
||||
list($range_begin, $range_end) = $response->parseHTTPRange($range);
|
||||
|
||||
if ($range_begin !== null) {
|
||||
if ($range_end !== null) {
|
||||
$data = substr($data, $range_begin, ($range_end - $range_begin));
|
||||
} else {
|
||||
$data = substr($data, $range_begin);
|
||||
}
|
||||
}
|
||||
|
||||
$response->setContentIterator(array($data));
|
||||
} else {
|
||||
$response
|
||||
->setContent($data)
|
||||
->setCompressResponse(true);
|
||||
}
|
||||
|
||||
|
||||
// NOTE: This is a piece of magic required to make WOFF fonts work in
|
||||
// Firefox and IE. Possibly we should generalize this more.
|
||||
|
@@ -14,7 +14,7 @@ abstract class CelerityResources extends Phobject {
|
||||
|
||||
public function getCelerityHash($data) {
|
||||
$tail = PhabricatorEnv::getEnvConfig('celerity.resource-hash');
|
||||
$hash = PhabricatorHash::digest($data, $tail);
|
||||
$hash = PhabricatorHash::weakDigest($data, $tail);
|
||||
return substr($hash, 0, 8);
|
||||
}
|
||||
|
||||
|
@@ -56,6 +56,12 @@ abstract class PhabricatorConduitController extends PhabricatorController {
|
||||
$panel_link),
|
||||
);
|
||||
|
||||
if ($params === null) {
|
||||
$messages[] = pht(
|
||||
'If you submit parameters, these examples will update to show '.
|
||||
'exactly how to encode the parameters you submit.');
|
||||
}
|
||||
|
||||
$info_view = id(new PHUIInfoView())
|
||||
->setErrors($messages)
|
||||
->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
|
||||
|
@@ -27,7 +27,7 @@ final class PhabricatorConfigApplication extends PhabricatorApplication {
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
return 'Config';
|
||||
return pht('Config');
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
@@ -69,6 +69,7 @@ final class PhabricatorConfigApplication extends PhabricatorApplication {
|
||||
'databases/' => 'PhabricatorConfigClusterDatabasesController',
|
||||
'notifications/' => 'PhabricatorConfigClusterNotificationsController',
|
||||
'repositories/' => 'PhabricatorConfigClusterRepositoriesController',
|
||||
'search/' => 'PhabricatorConfigClusterSearchController',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorElasticSearchSetupCheck extends PhabricatorSetupCheck {
|
||||
|
||||
public function getDefaultGroup() {
|
||||
return self::GROUP_OTHER;
|
||||
}
|
||||
|
||||
protected function executeChecks() {
|
||||
if (!$this->shouldUseElasticSearchEngine()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$engine = new PhabricatorElasticFulltextStorageEngine();
|
||||
|
||||
$index_exists = null;
|
||||
$index_sane = null;
|
||||
try {
|
||||
$index_exists = $engine->indexExists();
|
||||
if ($index_exists) {
|
||||
$index_sane = $engine->indexIsSane();
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$summary = pht('Elasticsearch is not reachable as configured.');
|
||||
$message = pht(
|
||||
'Elasticsearch is configured (with the %s setting) but Phabricator '.
|
||||
'encountered an exception when trying to test the index.'.
|
||||
"\n\n".
|
||||
'%s',
|
||||
phutil_tag('tt', array(), 'search.elastic.host'),
|
||||
phutil_tag('pre', array(), $ex->getMessage()));
|
||||
|
||||
$this->newIssue('elastic.misconfigured')
|
||||
->setName(pht('Elasticsearch Misconfigured'))
|
||||
->setSummary($summary)
|
||||
->setMessage($message)
|
||||
->addRelatedPhabricatorConfig('search.elastic.host');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$index_exists) {
|
||||
$summary = pht(
|
||||
'You enabled Elasticsearch but the index does not exist.');
|
||||
|
||||
$message = pht(
|
||||
'You likely enabled search.elastic.host without creating the '.
|
||||
'index. Run `./bin/search init` to correct the index.');
|
||||
|
||||
$this
|
||||
->newIssue('elastic.missing-index')
|
||||
->setName(pht('Elasticsearch index Not Found'))
|
||||
->setSummary($summary)
|
||||
->setMessage($message)
|
||||
->addRelatedPhabricatorConfig('search.elastic.host');
|
||||
} else if (!$index_sane) {
|
||||
$summary = pht(
|
||||
'Elasticsearch index exists but needs correction.');
|
||||
|
||||
$message = pht(
|
||||
'Either the Phabricator schema for Elasticsearch has changed '.
|
||||
'or Elasticsearch created the index automatically. Run '.
|
||||
'`./bin/search init` to correct the index.');
|
||||
|
||||
$this
|
||||
->newIssue('elastic.broken-index')
|
||||
->setName(pht('Elasticsearch index Incorrect'))
|
||||
->setSummary($summary)
|
||||
->setMessage($message);
|
||||
}
|
||||
}
|
||||
|
||||
protected function shouldUseElasticSearchEngine() {
|
||||
$search_engine = PhabricatorFulltextStorageEngine::loadEngine();
|
||||
return ($search_engine instanceof PhabricatorElasticFulltextStorageEngine);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorElasticsearchSetupCheck extends PhabricatorSetupCheck {
|
||||
|
||||
public function getDefaultGroup() {
|
||||
return self::GROUP_OTHER;
|
||||
}
|
||||
|
||||
protected function executeChecks() {
|
||||
$services = PhabricatorSearchService::getAllServices();
|
||||
|
||||
foreach ($services as $service) {
|
||||
try {
|
||||
$host = $service->getAnyHostForRole('read');
|
||||
} catch (PhabricatorClusterNoHostForRoleException $e) {
|
||||
// ignore the error
|
||||
continue;
|
||||
}
|
||||
if ($host instanceof PhabricatorElasticsearchHost) {
|
||||
$index_exists = null;
|
||||
$index_sane = null;
|
||||
try {
|
||||
$engine = $host->getEngine();
|
||||
$index_exists = $engine->indexExists();
|
||||
if ($index_exists) {
|
||||
$index_sane = $engine->indexIsSane();
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$summary = pht('Elasticsearch is not reachable as configured.');
|
||||
$message = pht(
|
||||
'Elasticsearch is configured (with the %s setting) but Phabricator'.
|
||||
' encountered an exception when trying to test the index.'.
|
||||
"\n\n".
|
||||
'%s',
|
||||
phutil_tag('tt', array(), 'cluster.search'),
|
||||
phutil_tag('pre', array(), $ex->getMessage()));
|
||||
|
||||
$this->newIssue('elastic.misconfigured')
|
||||
->setName(pht('Elasticsearch Misconfigured'))
|
||||
->setSummary($summary)
|
||||
->setMessage($message)
|
||||
->addRelatedPhabricatorConfig('cluster.search');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$index_exists) {
|
||||
$summary = pht(
|
||||
'You enabled Elasticsearch but the index does not exist.');
|
||||
|
||||
$message = pht(
|
||||
'You likely enabled cluster.search without creating the '.
|
||||
'index. Use the following command to create a new index.');
|
||||
|
||||
$this
|
||||
->newIssue('elastic.missing-index')
|
||||
->setName(pht('Elasticsearch Index Not Found'))
|
||||
->addCommand('./bin/search init')
|
||||
->setSummary($summary)
|
||||
->setMessage($message);
|
||||
|
||||
} else if (!$index_sane) {
|
||||
$summary = pht(
|
||||
'Elasticsearch index exists but needs correction.');
|
||||
|
||||
$message = pht(
|
||||
'Either the Phabricator schema for Elasticsearch has changed '.
|
||||
'or Elasticsearch created the index automatically. '.
|
||||
'Use the following command to rebuild the index.');
|
||||
|
||||
$this
|
||||
->newIssue('elastic.broken-index')
|
||||
->setName(pht('Elasticsearch Index Schema Mismatch'))
|
||||
->addCommand('./bin/search init')
|
||||
->setSummary($summary)
|
||||
->setMessage($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -198,6 +198,10 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
||||
'This option has been removed, you can use Dashboards to provide '.
|
||||
'homepage customization. See T11533 for more details.');
|
||||
|
||||
$elastic_reason = pht(
|
||||
'Elasticsearch is now configured with "%s".',
|
||||
'cluster.search');
|
||||
|
||||
$ancient_config += array(
|
||||
'phid.external-loaders' =>
|
||||
pht(
|
||||
@@ -348,6 +352,10 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
||||
'mysql.configuration-provider' => pht(
|
||||
'Phabricator now has application-level management of partitioning '.
|
||||
'and replicas.'),
|
||||
|
||||
'search.elastic.host' => $elastic_reason,
|
||||
'search.elastic.namespace' => $elastic_reason,
|
||||
|
||||
);
|
||||
|
||||
return $ancient_config;
|
||||
|
@@ -145,7 +145,7 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck {
|
||||
"be able to find search results for common words. You can gain ".
|
||||
"access to this option by upgrading MySQL to a more recent ".
|
||||
"version.\n\n".
|
||||
"You can ignore this warning if you plan to configure ElasticSearch ".
|
||||
"You can ignore this warning if you plan to configure Elasticsearch ".
|
||||
"later, or aren't concerned about searching for common words.",
|
||||
$host_name,
|
||||
phutil_tag('tt', array(), 'ft_stopword_file'));
|
||||
@@ -180,7 +180,7 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck {
|
||||
"To make search more useful, you can use an alternate stopword ".
|
||||
"file with fewer words. Alternatively, if you aren't concerned ".
|
||||
"about searching for common words, you can ignore this warning. ".
|
||||
"If you later plan to configure ElasticSearch, you can also ignore ".
|
||||
"If you later plan to configure Elasticsearch, you can also ignore ".
|
||||
"this warning: this stopword file only affects MySQL fulltext ".
|
||||
"indexes.\n\n".
|
||||
"To choose a different stopword file, add this to your %s file ".
|
||||
@@ -231,7 +231,7 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck {
|
||||
"You can change this setting to 3 to allow these words to be ".
|
||||
"indexed. Alternatively, you can ignore this warning if you are ".
|
||||
"not concerned about searching for 3-letter words. If you later ".
|
||||
"plan to configure ElasticSearch, you can also ignore this warning: ".
|
||||
"plan to configure Elasticsearch, you can also ignore this warning: ".
|
||||
"only MySQL fulltext search is affected.\n\n".
|
||||
"To reduce the minimum word length to 3, add this to your %s file ".
|
||||
"(in the %s section) and then restart %s:\n\n".
|
||||
@@ -379,8 +379,13 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck {
|
||||
}
|
||||
|
||||
protected function shouldUseMySQLSearchEngine() {
|
||||
$search_engine = PhabricatorFulltextStorageEngine::loadEngine();
|
||||
return ($search_engine instanceof PhabricatorMySQLFulltextStorageEngine);
|
||||
$services = PhabricatorSearchService::getAllServices();
|
||||
foreach ($services as $service) {
|
||||
if ($service instanceof PhabricatorMySQLSearchHost) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -107,7 +107,7 @@ final class PhabricatorPathSetupCheck extends PhabricatorSetupCheck {
|
||||
|
||||
if ($bad_paths) {
|
||||
foreach ($bad_paths as $path_part => $message) {
|
||||
$digest = substr(PhabricatorHash::digest($path_part), 0, 8);
|
||||
$digest = substr(PhabricatorHash::weakDigest($path_part), 0, 8);
|
||||
|
||||
$this
|
||||
->newIssue('config.PATH.'.$digest)
|
||||
|
@@ -103,10 +103,20 @@ final class PhabricatorConfigClusterNotificationsController
|
||||
new PhutilNumber(idx($details, 'messages.in')),
|
||||
new PhutilNumber(idx($details, 'messages.out')));
|
||||
|
||||
if (idx($details, 'history.size')) {
|
||||
$history = pht(
|
||||
'%s Held / %sms',
|
||||
new PhutilNumber(idx($details, 'history.size')),
|
||||
new PhutilNumber(idx($details, 'history.age')));
|
||||
} else {
|
||||
$history = pht('No Messages');
|
||||
}
|
||||
|
||||
} else {
|
||||
$uptime = null;
|
||||
$clients = null;
|
||||
$stats = null;
|
||||
$history = null;
|
||||
}
|
||||
|
||||
$status_view = array(
|
||||
@@ -126,6 +136,7 @@ final class PhabricatorConfigClusterNotificationsController
|
||||
$uptime,
|
||||
$clients,
|
||||
$stats,
|
||||
$history,
|
||||
$messages,
|
||||
);
|
||||
}
|
||||
@@ -143,6 +154,7 @@ final class PhabricatorConfigClusterNotificationsController
|
||||
pht('Uptime'),
|
||||
pht('Clients'),
|
||||
pht('Messages'),
|
||||
pht('History'),
|
||||
null,
|
||||
))
|
||||
->setColumnClasses(
|
||||
@@ -155,6 +167,7 @@ final class PhabricatorConfigClusterNotificationsController
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
'wide',
|
||||
));
|
||||
|
||||
|
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorConfigClusterSearchController
|
||||
extends PhabricatorConfigController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$nav = $this->buildSideNavView();
|
||||
$nav->selectFilter('cluster/search/');
|
||||
|
||||
$title = pht('Cluster Search');
|
||||
$doc_href = PhabricatorEnv::getDoclink('Cluster: Search');
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($title)
|
||||
->setProfileHeader(true)
|
||||
->addActionLink(
|
||||
id(new PHUIButtonView())
|
||||
->setIcon('fa-book')
|
||||
->setHref($doc_href)
|
||||
->setTag('a')
|
||||
->setText(pht('Documentation')));
|
||||
|
||||
$crumbs = $this
|
||||
->buildApplicationCrumbs($nav)
|
||||
->addTextCrumb($title)
|
||||
->setBorder(true);
|
||||
|
||||
$search_status = $this->buildClusterSearchStatus();
|
||||
|
||||
$content = id(new PhabricatorConfigPageView())
|
||||
->setHeader($header)
|
||||
->setContent($search_status);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->setNavigation($nav)
|
||||
->appendChild($content)
|
||||
->addClass('white-background');
|
||||
}
|
||||
|
||||
private function buildClusterSearchStatus() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$services = PhabricatorSearchService::getAllServices();
|
||||
Javelin::initBehavior('phabricator-tooltips');
|
||||
|
||||
$view = array();
|
||||
foreach ($services as $service) {
|
||||
$view[] = $this->renderStatusView($service);
|
||||
}
|
||||
return $view;
|
||||
}
|
||||
|
||||
private function renderStatusView($service) {
|
||||
$head = array_merge(
|
||||
array(pht('Type')),
|
||||
array_keys($service->getStatusViewColumns()),
|
||||
array(pht('Status')));
|
||||
|
||||
$rows = array();
|
||||
|
||||
$status_map = PhabricatorSearchService::getConnectionStatusMap();
|
||||
$stats = false;
|
||||
$stats_view = false;
|
||||
|
||||
foreach ($service->getHosts() as $host) {
|
||||
try {
|
||||
$status = $host->getConnectionStatus();
|
||||
$status = idx($status_map, $status, array());
|
||||
} catch (Exception $ex) {
|
||||
$status['icon'] = 'fa-times';
|
||||
$status['label'] = pht('Connection Error');
|
||||
$status['color'] = 'red';
|
||||
$host->didHealthCheck(false);
|
||||
}
|
||||
|
||||
if (!$stats_view) {
|
||||
try {
|
||||
$stats = $host->getEngine()->getIndexStats($host);
|
||||
$stats_view = $this->renderIndexStats($stats);
|
||||
} catch (Exception $e) {
|
||||
$stats_view = false;
|
||||
}
|
||||
}
|
||||
|
||||
$type_icon = 'fa-search sky';
|
||||
$type_tip = $host->getDisplayName();
|
||||
|
||||
$type_icon = id(new PHUIIconView())
|
||||
->setIcon($type_icon);
|
||||
$status_view = array(
|
||||
id(new PHUIIconView())->setIcon($status['icon'].' '.$status['color']),
|
||||
' ',
|
||||
$status['label'],
|
||||
);
|
||||
$row = array(array($type_icon, ' ', $type_tip));
|
||||
$row = array_merge($row, array_values(
|
||||
$host->getStatusViewColumns()));
|
||||
$row[] = $status_view;
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setNoDataString(pht('No search servers are configured.'))
|
||||
->setHeaders($head);
|
||||
|
||||
$view = id(new PHUIObjectBoxView())
|
||||
->setHeaderText($service->getDisplayName())
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($table);
|
||||
|
||||
if ($stats_view) {
|
||||
$view->addPropertyList($stats_view);
|
||||
}
|
||||
return $view;
|
||||
}
|
||||
|
||||
private function renderIndexStats($stats) {
|
||||
$view = id(new PHUIPropertyListView());
|
||||
if ($stats !== false) {
|
||||
foreach ($stats as $label => $val) {
|
||||
$view->addProperty($label, $val);
|
||||
}
|
||||
}
|
||||
return $view;
|
||||
}
|
||||
|
||||
}
|
@@ -42,8 +42,11 @@ abstract class PhabricatorConfigController extends PhabricatorController {
|
||||
pht('Notification Servers'), null, 'fa-bell-o');
|
||||
$nav->addFilter('cluster/repositories/',
|
||||
pht('Repository Servers'), null, 'fa-code');
|
||||
$nav->addFilter('cluster/search/',
|
||||
pht('Search Servers'), null, 'fa-search');
|
||||
$nav->addLabel(pht('Modules'));
|
||||
|
||||
|
||||
$modules = PhabricatorConfigModule::getAllModules();
|
||||
foreach ($modules as $key => $module) {
|
||||
$nav->addFilter('module/'.$key.'/',
|
||||
|
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
class PhabricatorConfigRegexOptionType
|
||||
extends PhabricatorConfigJSONOptionType {
|
||||
|
||||
public function validateOption(PhabricatorConfigOption $option, $value) {
|
||||
foreach ($value as $pattern => $spec) {
|
||||
$ok = preg_match($pattern, '');
|
||||
if ($ok === false) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'The following regex is malformed and cannot be used: %s',
|
||||
$pattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -35,6 +35,7 @@ final class PhabricatorAccessLogConfigOptions
|
||||
'P' => pht('The logged-in user PHID, if one is logged in.'),
|
||||
'i' => pht('Request input, in bytes.'),
|
||||
'o' => pht('Request output, in bytes.'),
|
||||
'I' => pht('Cluster instance name, if configured.'),
|
||||
);
|
||||
|
||||
$http_map = $common_map + array(
|
||||
|
@@ -38,6 +38,17 @@ EOTEXT
|
||||
$intro_href = PhabricatorEnv::getDoclink('Clustering Introduction');
|
||||
$intro_name = pht('Clustering Introduction');
|
||||
|
||||
$search_type = 'custom:PhabricatorClusterSearchConfigOptionType';
|
||||
$search_help = $this->deformat(pht(<<<EOTEXT
|
||||
Define one or more fulltext storage services. Here you can configure which
|
||||
hosts will handle fulltext search queries and indexing. For help with
|
||||
configuring fulltext search clusters, see **[[ %s | %s ]]** in the
|
||||
documentation.
|
||||
EOTEXT
|
||||
,
|
||||
PhabricatorEnv::getDoclink('Cluster: Search'),
|
||||
pht('Cluster: Search')));
|
||||
|
||||
return array(
|
||||
$this->newOption('cluster.addresses', 'list<string>', array())
|
||||
->setLocked(true)
|
||||
@@ -114,6 +125,21 @@ EOTEXT
|
||||
->setSummary(
|
||||
pht('Configure database read replicas.'))
|
||||
->setDescription($databases_help),
|
||||
$this->newOption('cluster.search', $search_type, array())
|
||||
->setLocked(true)
|
||||
->setSummary(
|
||||
pht('Configure full-text search services.'))
|
||||
->setDescription($search_help)
|
||||
->setDefault(
|
||||
array(
|
||||
array(
|
||||
'type' => 'mysql',
|
||||
'roles' => array(
|
||||
'read' => true,
|
||||
'write' => true,
|
||||
),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -109,7 +109,8 @@ EOTEXT
|
||||
'Default key for HMAC digests where the key is not important '.
|
||||
'(i.e., the hash itself is secret). You can change this if you '.
|
||||
'want (to any other string), but doing so will break existing '.
|
||||
'sessions and CSRF tokens.')),
|
||||
'sessions and CSRF tokens. This option is deprecated. Newer '.
|
||||
'code automatically manages HMAC keys.')),
|
||||
$this->newOption('security.require-https', 'bool', false)
|
||||
->setLocked(true)
|
||||
->setSummary(
|
||||
|
@@ -120,11 +120,11 @@ final class PhabricatorSyntaxHighlightingConfigOptions
|
||||
'this is where that list is defined.')),
|
||||
$this->newOption(
|
||||
'syntax.filemap',
|
||||
'wild',
|
||||
'custom:PhabricatorConfigRegexOptionType',
|
||||
array(
|
||||
'@\.arcconfig$@' => 'js',
|
||||
'@\.arclint$@' => 'js',
|
||||
'@\.divinerconfig$@' => 'js',
|
||||
'@\.arcconfig$@' => 'json',
|
||||
'@\.arclint$@' => 'json',
|
||||
'@\.divinerconfig$@' => 'json',
|
||||
))
|
||||
->setSummary(
|
||||
pht('Override what language files (based on filename) highlight as.'))
|
||||
@@ -138,12 +138,14 @@ final class PhabricatorSyntaxHighlightingConfigOptions
|
||||
'be tested against the filename. They should map to either an '.
|
||||
'explicit language as a string value, or a numeric index into '.
|
||||
'the captured groups as an integer.'))
|
||||
->addExample('{"@\\.xyz$@": "php"}', pht('Highlight %s as PHP.', '*.xyz'))
|
||||
->addExample(
|
||||
'{"@/httpd\\.conf@": "apacheconf"}',
|
||||
'{"@\\\.xyz$@": "php"}',
|
||||
pht('Highlight %s as PHP.', '*.xyz'))
|
||||
->addExample(
|
||||
'{"@/httpd\\\.conf@": "apacheconf"}',
|
||||
pht('Highlight httpd.conf as "apacheconf".'))
|
||||
->addExample(
|
||||
'{"@\\.([^.]+)\\.bak$@": 1}',
|
||||
'{"@\\\.([^.]+)\\\.bak$@": 1}',
|
||||
pht(
|
||||
"Treat all '*.x.bak' file as '.x'. NOTE: We map to capturing group ".
|
||||
"1 by specifying the mapping as '1'")),
|
||||
|
@@ -11,6 +11,13 @@ final class PhabricatorConfigDatabaseSchema
|
||||
public function addTable(PhabricatorConfigTableSchema $table) {
|
||||
$key = $table->getName();
|
||||
if (isset($this->tables[$key])) {
|
||||
|
||||
if ($key == 'application_application') {
|
||||
// NOTE: This is a terrible hack to allow Application subclasses to
|
||||
// extend LiskDAO so we can apply transactions to them.
|
||||
return $this;
|
||||
}
|
||||
|
||||
throw new Exception(
|
||||
pht('Trying to add duplicate table "%s"!', $key));
|
||||
}
|
||||
|
@@ -60,7 +60,8 @@ final class ConpherenceTransactionRenderer extends Phobject {
|
||||
// between days. some setup required!
|
||||
$previous_transaction = null;
|
||||
$date_marker_transaction = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_DATE_MARKER)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadDateMarkerTransaction::TRANSACTIONTYPE)
|
||||
->makeEphemeral();
|
||||
$date_marker_transaction_view = id(new ConpherenceTransactionView())
|
||||
->setUser($user)
|
||||
|
@@ -16,9 +16,6 @@ final class ConpherenceRoomTestCase extends ConpherenceTestCase {
|
||||
|
||||
$this->assertTrue((bool)$conpherence->getID());
|
||||
$this->assertEqual(1, count($conpherence->getParticipants()));
|
||||
$this->assertEqual(
|
||||
$participant_phids,
|
||||
$conpherence->getRecentParticipantPHIDs());
|
||||
}
|
||||
|
||||
public function testNUserRoomCreate() {
|
||||
@@ -38,9 +35,6 @@ final class ConpherenceRoomTestCase extends ConpherenceTestCase {
|
||||
|
||||
$this->assertTrue((bool)$conpherence->getID());
|
||||
$this->assertEqual(4, count($conpherence->getParticipants()));
|
||||
$this->assertEqual(
|
||||
$participant_phids,
|
||||
$conpherence->getRecentParticipantPHIDs());
|
||||
}
|
||||
|
||||
public function testRoomParticipantAddition() {
|
||||
@@ -58,16 +52,11 @@ final class ConpherenceRoomTestCase extends ConpherenceTestCase {
|
||||
|
||||
$this->assertTrue((bool)$conpherence->getID());
|
||||
$this->assertEqual(2, count($conpherence->getParticipants()));
|
||||
$this->assertEqual(
|
||||
$participant_phids,
|
||||
$conpherence->getRecentParticipantPHIDs());
|
||||
|
||||
// test add by creator
|
||||
$participant_phids[] = $friend_2->getPHID();
|
||||
$this->addParticipants($creator, $conpherence, array($friend_2->getPHID()));
|
||||
$this->assertEqual(
|
||||
$participant_phids,
|
||||
$conpherence->getRecentParticipantPHIDs());
|
||||
$this->assertEqual(3, count($conpherence->getParticipants()));
|
||||
|
||||
// test add by other participant, so recent participation should
|
||||
// meaningfully change
|
||||
@@ -81,9 +70,7 @@ final class ConpherenceRoomTestCase extends ConpherenceTestCase {
|
||||
$friend_2,
|
||||
$conpherence,
|
||||
array($friend_3->getPHID()));
|
||||
$this->assertEqual(
|
||||
$participant_phids,
|
||||
$conpherence->getRecentParticipantPHIDs());
|
||||
$this->assertEqual(4, count($conpherence->getParticipants()));
|
||||
}
|
||||
|
||||
public function testRoomParticipantDeletion() {
|
||||
@@ -120,10 +107,12 @@ final class ConpherenceRoomTestCase extends ConpherenceTestCase {
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $participant_phids));
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_TITLE)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadTitleTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(pht('Test'));
|
||||
|
||||
id(new ConpherenceEditor())
|
||||
|
@@ -9,7 +9,8 @@ abstract class ConpherenceTestCase extends PhabricatorTestCase {
|
||||
|
||||
$xactions = array(
|
||||
id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $participant_phids)),
|
||||
);
|
||||
$editor = id(new ConpherenceEditor())
|
||||
@@ -26,7 +27,8 @@ abstract class ConpherenceTestCase extends PhabricatorTestCase {
|
||||
|
||||
$xactions = array(
|
||||
id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('-' => $participant_phids)),
|
||||
);
|
||||
$editor = id(new ConpherenceEditor())
|
||||
|
@@ -55,6 +55,8 @@ final class PhabricatorConpherenceApplication extends PhabricatorApplication {
|
||||
=> 'ConpherenceNotificationPanelController',
|
||||
'participant/(?P<id>[1-9]\d*)/'
|
||||
=> 'ConpherenceParticipantController',
|
||||
'preferences/(?P<id>[1-9]\d*)/'
|
||||
=> 'ConpherenceRoomPreferencesController',
|
||||
'update/(?P<id>[1-9]\d*)/'
|
||||
=> 'ConpherenceUpdateController',
|
||||
),
|
||||
|
@@ -36,8 +36,7 @@ final class ConpherenceQueryThreadConduitAPIMethod
|
||||
$offset = $request->getValue('offset');
|
||||
|
||||
$query = id(new ConpherenceThreadQuery())
|
||||
->setViewer($user)
|
||||
->needParticipantCache(true);
|
||||
->setViewer($user);
|
||||
|
||||
if ($ids) {
|
||||
$conpherences = $query
|
||||
@@ -57,7 +56,7 @@ final class ConpherenceQueryThreadConduitAPIMethod
|
||||
->setLimit($limit)
|
||||
->setOffset($offset)
|
||||
->execute();
|
||||
$conpherence_phids = array_keys($participation);
|
||||
$conpherence_phids = mpull($participation, 'getConpherencePHID');
|
||||
$query->withPHIDs($conpherence_phids);
|
||||
$conpherences = $query->execute();
|
||||
$conpherences = array_select_keys($conpherences, $conpherence_phids);
|
||||
@@ -71,7 +70,6 @@ final class ConpherenceQueryThreadConduitAPIMethod
|
||||
'conpherencePHID' => $conpherence->getPHID(),
|
||||
'conpherenceTitle' => $conpherence->getTitle(),
|
||||
'messageCount' => $conpherence->getMessageCount(),
|
||||
'recentParticipantPHIDs' => $conpherence->getRecentParticipantPHIDs(),
|
||||
'conpherenceURI' => $this->getConpherenceURI($conpherence),
|
||||
);
|
||||
}
|
||||
|
@@ -69,7 +69,7 @@ final class ConpherenceUpdateThreadConduitAPIMethod
|
||||
if ($add_participant_phids) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(
|
||||
ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $add_participant_phids));
|
||||
}
|
||||
if ($remove_participant_phid) {
|
||||
@@ -78,12 +78,13 @@ final class ConpherenceUpdateThreadConduitAPIMethod
|
||||
}
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(
|
||||
ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('-' => array($remove_participant_phid)));
|
||||
}
|
||||
if ($title) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_TITLE)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadTitleTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($title);
|
||||
}
|
||||
if ($message) {
|
||||
|
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
final class ConpherenceParticipationStatus extends ConpherenceConstants {
|
||||
|
||||
const UP_TO_DATE = 0;
|
||||
const BEHIND = 1;
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
final class ConpherenceRoomSettings extends ConpherenceConstants {
|
||||
|
||||
const SOUND_RECEIVE = 'receive';
|
||||
const SOUND_MENTION = 'mention';
|
||||
|
||||
const DEFAULT_RECEIVE_SOUND = 'tap';
|
||||
const DEFAULT_MENTION_SOUND = 'alert';
|
||||
const DEFAULT_NO_SOUND = 'none';
|
||||
|
||||
const COLOR_LIGHT = 'light';
|
||||
const COLOR_BLUE = 'blue';
|
||||
const COLOR_INDIGO = 'indigo';
|
||||
const COLOR_PEACH = 'peach';
|
||||
const COLOR_GREEN = 'green';
|
||||
const COLOR_PINK = 'pink';
|
||||
|
||||
public static function getSoundMap() {
|
||||
return array(
|
||||
'none' => array(
|
||||
'name' => pht('No Sound'),
|
||||
'rsrc' => '',
|
||||
),
|
||||
'alert' => array(
|
||||
'name' => pht('Alert'),
|
||||
'rsrc' => celerity_get_resource_uri('/rsrc/audio/basic/alert.mp3'),
|
||||
),
|
||||
'bing' => array(
|
||||
'name' => pht('Bing'),
|
||||
'rsrc' => celerity_get_resource_uri('/rsrc/audio/basic/bing.mp3'),
|
||||
),
|
||||
'pock' => array(
|
||||
'name' => pht('Pock'),
|
||||
'rsrc' => celerity_get_resource_uri('/rsrc/audio/basic/pock.mp3'),
|
||||
),
|
||||
'tap' => array(
|
||||
'name' => pht('Tap'),
|
||||
'rsrc' => celerity_get_resource_uri('/rsrc/audio/basic/tap.mp3'),
|
||||
),
|
||||
'ting' => array(
|
||||
'name' => pht('Ting'),
|
||||
'rsrc' => celerity_get_resource_uri('/rsrc/audio/basic/ting.mp3'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public static function getDropdownSoundMap() {
|
||||
$map = self::getSoundMap();
|
||||
return ipull($map, 'name');
|
||||
}
|
||||
|
||||
public static function getThemeMap() {
|
||||
return array(
|
||||
self::COLOR_LIGHT => pht('Light'),
|
||||
self::COLOR_BLUE => pht('Blue'),
|
||||
self::COLOR_INDIGO => pht('Indigo'),
|
||||
self::COLOR_PEACH => pht('Peach'),
|
||||
self::COLOR_GREEN => pht('Green'),
|
||||
self::COLOR_PINK => pht('Pink'),
|
||||
);
|
||||
}
|
||||
|
||||
public static function getThemeClass($theme) {
|
||||
return 'conpherence-theme-'.$theme;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -8,7 +8,6 @@ final class ConpherenceUpdateActions extends ConpherenceConstants {
|
||||
const JOIN_ROOM = 'join_room';
|
||||
const ADD_PERSON = 'add_person';
|
||||
const REMOVE_PERSON = 'remove_person';
|
||||
const NOTIFICATIONS = 'notifications';
|
||||
const ADD_STATUS = 'add_status';
|
||||
const LOAD = 'load';
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@ final class ConpherenceColumnViewController extends
|
||||
->setViewer($user)
|
||||
->withPHIDs($conpherence_phids)
|
||||
->needProfileImage(true)
|
||||
->needParticipantCache(true)
|
||||
->execute();
|
||||
$latest_conpherences = mpull($latest_conpherences, null, 'getPHID');
|
||||
$latest_conpherences = array_select_keys(
|
||||
@@ -68,7 +67,7 @@ final class ConpherenceColumnViewController extends
|
||||
$transactions = $conpherence->getTransactions();
|
||||
$latest_transaction = head($transactions);
|
||||
$write_guard = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$participant->markUpToDate($conpherence, $latest_transaction);
|
||||
$participant->markUpToDate($conpherence);
|
||||
unset($write_guard);
|
||||
|
||||
$draft = PhabricatorDraft::newFromUserAndKey(
|
||||
|
@@ -54,19 +54,29 @@ abstract class ConpherenceController extends PhabricatorController {
|
||||
}
|
||||
|
||||
protected function buildHeaderPaneContent(
|
||||
ConpherenceThread $conpherence,
|
||||
array $policy_objects) {
|
||||
assert_instances_of($policy_objects, 'PhabricatorPolicy');
|
||||
ConpherenceThread $conpherence) {
|
||||
$viewer = $this->getViewer();
|
||||
$header = null;
|
||||
$id = $conpherence->getID();
|
||||
|
||||
if ($conpherence->getID()) {
|
||||
if ($id) {
|
||||
$data = $conpherence->getDisplayData($this->getViewer());
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setViewer($viewer)
|
||||
->setHeader($data['title'])
|
||||
->setSubheader($data['topic'])
|
||||
->setPolicyObject($conpherence)
|
||||
->setImage($data['image']);
|
||||
|
||||
if (strlen($data['topic'])) {
|
||||
$topic = id(new PHUITagView())
|
||||
->setName($data['topic'])
|
||||
->setShade(PHUITagView::COLOR_VIOLET)
|
||||
->setType(PHUITagView::TYPE_SHADE)
|
||||
->addClass('conpherence-header-topic');
|
||||
$header->addTag($topic);
|
||||
}
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$conpherence,
|
||||
@@ -74,19 +84,14 @@ abstract class ConpherenceController extends PhabricatorController {
|
||||
|
||||
if ($can_edit) {
|
||||
$header->setImageURL(
|
||||
$this->getApplicationURI('picture/'.$conpherence->getID().'/'));
|
||||
$this->getApplicationURI("picture/{$id}/"));
|
||||
}
|
||||
|
||||
$participating = $conpherence->getParticipantIfExists($viewer->getPHID());
|
||||
$can_join = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$conpherence,
|
||||
PhabricatorPolicyCapability::CAN_JOIN);
|
||||
|
||||
$header->addActionItem(
|
||||
id(new PHUIIconCircleView())
|
||||
->setHref(
|
||||
$this->getApplicationURI('update/'.$conpherence->getID()).'/')
|
||||
->setHref($this->getApplicationURI("update/{$id}/"))
|
||||
->setIcon('fa-pencil')
|
||||
->addClass('hide-on-device')
|
||||
->setColor('violet')
|
||||
@@ -94,9 +99,7 @@ abstract class ConpherenceController extends PhabricatorController {
|
||||
|
||||
$header->addActionItem(
|
||||
id(new PHUIIconCircleView())
|
||||
->setHref(
|
||||
$this->getApplicationURI('update/'.$conpherence->getID()).'/'.
|
||||
'?action='.ConpherenceUpdateActions::NOTIFICATIONS)
|
||||
->setHref($this->getApplicationURI("preferences/{$id}/"))
|
||||
->setIcon('fa-gear')
|
||||
->addClass('hide-on-device')
|
||||
->setColor('pink')
|
||||
@@ -129,9 +132,9 @@ abstract class ConpherenceController extends PhabricatorController {
|
||||
->setColor('green')
|
||||
->addClass('conpherence-search-toggle'));
|
||||
|
||||
if ($can_join && !$participating) {
|
||||
if (!$participating) {
|
||||
$action = ConpherenceUpdateActions::JOIN_ROOM;
|
||||
$uri = $this->getApplicationURI('update/'.$conpherence->getID().'/');
|
||||
$uri = $this->getApplicationURI("update/{$id}/");
|
||||
$button = phutil_tag(
|
||||
'button',
|
||||
array(
|
||||
|
@@ -34,7 +34,7 @@ final class ConpherenceListController extends ConpherenceController {
|
||||
$title = pht('Conpherence');
|
||||
$conpherence = null;
|
||||
|
||||
$limit = (ConpherenceThreadListView::SEE_MORE_LIMIT * 2) + 1;
|
||||
$limit = ConpherenceThreadListView::SEE_ALL_LIMIT + 1;
|
||||
$all_participation = array();
|
||||
|
||||
$mode = $this->determineMode();
|
||||
@@ -64,7 +64,7 @@ final class ConpherenceListController extends ConpherenceController {
|
||||
}
|
||||
|
||||
// check to see if the loaded conpherence is going to show up
|
||||
// within the SEE_MORE_LIMIT amount of conpherences.
|
||||
// within the SEE_ALL_LIMIT amount of conpherences.
|
||||
// If its not there, then we just pre-pend it as the "first"
|
||||
// conpherence so folks have a navigation item in the menu.
|
||||
$count = 0;
|
||||
@@ -75,7 +75,7 @@ final class ConpherenceListController extends ConpherenceController {
|
||||
break;
|
||||
}
|
||||
$count++;
|
||||
if ($count > ConpherenceThreadListView::SEE_MORE_LIMIT) {
|
||||
if ($count > ConpherenceThreadListView::SEE_ALL_LIMIT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -89,11 +89,19 @@ final class ConpherenceListController extends ConpherenceController {
|
||||
default:
|
||||
$data = $this->loadDefaultParticipation($limit);
|
||||
$all_participation = $data['all_participation'];
|
||||
if ($all_participation) {
|
||||
$conpherence_id = head($all_participation)->getConpherencePHID();
|
||||
$conpherence = id(new ConpherenceThreadQuery())
|
||||
->setViewer($user)
|
||||
->withPHIDs(array($conpherence_id))
|
||||
->needProfileImage(true)
|
||||
->executeOne();
|
||||
}
|
||||
// If $conpherence is null, NUX state will render
|
||||
break;
|
||||
}
|
||||
|
||||
$threads = $this->loadConpherenceThreadData(
|
||||
$all_participation);
|
||||
$threads = $this->loadConpherenceThreadData($all_participation);
|
||||
|
||||
$thread_view = id(new ConpherenceThreadListView())
|
||||
->setUser($user)
|
||||
@@ -144,6 +152,7 @@ final class ConpherenceListController extends ConpherenceController {
|
||||
->withParticipantPHIDs(array($viewer->getPHID()))
|
||||
->setLimit($limit)
|
||||
->execute();
|
||||
$all_participation = mpull($all_participation, null, 'getConpherencePHID');
|
||||
|
||||
return array(
|
||||
'all_participation' => $all_participation,
|
||||
@@ -159,7 +168,6 @@ final class ConpherenceListController extends ConpherenceController {
|
||||
->setViewer($user)
|
||||
->withPHIDs($conpherence_phids)
|
||||
->needProfileImage(true)
|
||||
->needParticipantCache(true)
|
||||
->execute();
|
||||
|
||||
// this will re-sort by participation data
|
||||
|
@@ -7,7 +7,6 @@ final class ConpherenceNewRoomController extends ConpherenceController {
|
||||
|
||||
$title = pht('New Room');
|
||||
$e_title = true;
|
||||
$v_message = null;
|
||||
$validation_exception = null;
|
||||
|
||||
$conpherence = ConpherenceThread::initializeNewRoom($user);
|
||||
@@ -17,17 +16,18 @@ final class ConpherenceNewRoomController extends ConpherenceController {
|
||||
$xactions = array();
|
||||
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_TITLE)
|
||||
->setTransactionType(ConpherenceThreadTitleTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($request->getStr('title'));
|
||||
|
||||
$participants = $request->getArr('participants');
|
||||
$participants[] = $user->getPHID();
|
||||
$participants = array_unique($participants);
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $participants));
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_TOPIC)
|
||||
->setTransactionType(ConpherenceThreadTopicTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($request->getStr('topic'));
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
|
||||
@@ -35,18 +35,6 @@ final class ConpherenceNewRoomController extends ConpherenceController {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
|
||||
->setNewValue($request->getStr('editPolicy'));
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_JOIN_POLICY)
|
||||
->setNewValue($request->getStr('joinPolicy'));
|
||||
|
||||
$v_message = $request->getStr('message');
|
||||
if (strlen($v_message)) {
|
||||
$message_xactions = $editor->generateTransactionsFromText(
|
||||
$user,
|
||||
$conpherence,
|
||||
$v_message);
|
||||
$xactions = array_merge($xactions, $message_xactions);
|
||||
}
|
||||
|
||||
try {
|
||||
$editor
|
||||
@@ -60,11 +48,11 @@ final class ConpherenceNewRoomController extends ConpherenceController {
|
||||
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||
$validation_exception = $ex;
|
||||
|
||||
$e_title = $ex->getShortMessage(ConpherenceTransaction::TYPE_TITLE);
|
||||
$e_title = $ex->getShortMessage(
|
||||
ConpherenceThreadTitleTransaction::TRANSACTIONTYPE);
|
||||
|
||||
$conpherence->setViewPolicy($request->getStr('viewPolicy'));
|
||||
$conpherence->setEditPolicy($request->getStr('editPolicy'));
|
||||
$conpherence->setJoinPolicy($request->getStr('joinPolicy'));
|
||||
}
|
||||
} else {
|
||||
if ($request->getStr('participant')) {
|
||||
@@ -119,19 +107,7 @@ final class ConpherenceNewRoomController extends ConpherenceController {
|
||||
->setName('editPolicy')
|
||||
->setPolicyObject($conpherence)
|
||||
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
|
||||
->setPolicies($policies))
|
||||
->appendChild(
|
||||
id(new AphrontFormPolicyControl())
|
||||
->setName('joinPolicy')
|
||||
->setPolicyObject($conpherence)
|
||||
->setCapability(PhabricatorPolicyCapability::CAN_JOIN)
|
||||
->setPolicies($policies))
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setUser($user)
|
||||
->setName('message')
|
||||
->setLabel(pht('First Message'))
|
||||
->setValue($v_message));
|
||||
->setPolicies($policies));
|
||||
|
||||
$dialog->appendChild($form);
|
||||
|
||||
|
@@ -7,12 +7,12 @@ final class ConpherenceNotificationPanelController
|
||||
$user = $request->getUser();
|
||||
$conpherences = array();
|
||||
require_celerity_resource('conpherence-notification-css');
|
||||
$unread_status = ConpherenceParticipationStatus::BEHIND;
|
||||
|
||||
$participant_data = id(new ConpherenceParticipantQuery())
|
||||
->withParticipantPHIDs(array($user->getPHID()))
|
||||
->setLimit(5)
|
||||
->execute();
|
||||
$participant_data = mpull($participant_data, null, 'getConpherencePHID');
|
||||
|
||||
if ($participant_data) {
|
||||
$conpherences = id(new ConpherenceThreadQuery())
|
||||
@@ -20,8 +20,7 @@ final class ConpherenceNotificationPanelController
|
||||
->withPHIDs(array_keys($participant_data))
|
||||
->needProfileImage(true)
|
||||
->needTransactions(true)
|
||||
->setTransactionLimit(50)
|
||||
->needParticipantCache(true)
|
||||
->setTransactionLimit(100)
|
||||
->execute();
|
||||
}
|
||||
|
||||
@@ -38,7 +37,7 @@ final class ConpherenceNotificationPanelController
|
||||
'conpherence-notification',
|
||||
);
|
||||
|
||||
if ($p_data->getParticipationStatus() == $unread_status) {
|
||||
if (!$p_data->isUpToDate($conpherence)) {
|
||||
$classes[] = 'phabricator-notification-unread';
|
||||
}
|
||||
$uri = $this->getApplicationURI($conpherence->getID().'/');
|
||||
@@ -96,7 +95,7 @@ final class ConpherenceNotificationPanelController
|
||||
|
||||
$unread = id(new ConpherenceParticipantCountQuery())
|
||||
->withParticipantPHIDs(array($user->getPHID()))
|
||||
->withParticipationStatus($unread_status)
|
||||
->withUnread(true)
|
||||
->execute();
|
||||
$unread_count = idx($unread, $user->getPHID(), 0);
|
||||
|
||||
|
@@ -76,7 +76,8 @@ final class ConpherenceRoomPictureController
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PICTURE)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadPictureTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($new_value);
|
||||
|
||||
$editor = id(new ConpherenceEditor())
|
||||
|
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
final class ConpherenceRoomPreferencesController
|
||||
extends ConpherenceController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$conpherence_id = $request->getURIData('id');
|
||||
|
||||
$conpherence = id(new ConpherenceThreadQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($conpherence_id))
|
||||
->executeOne();
|
||||
if (!$conpherence) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$view_uri = $conpherence->getURI();
|
||||
|
||||
$participant = $conpherence->getParticipantIfExists($viewer->getPHID());
|
||||
if (!$participant) {
|
||||
if ($viewer->isLoggedIn()) {
|
||||
$text = pht(
|
||||
'Notification settings are available after joining the room.');
|
||||
} else {
|
||||
$text = pht(
|
||||
'Notification settings are available after logging in and joining '.
|
||||
'the room.');
|
||||
}
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Room Preferences'))
|
||||
->addCancelButton($view_uri)
|
||||
->appendParagraph($text);
|
||||
}
|
||||
|
||||
// Save the data and redirect
|
||||
if ($request->isFormPost()) {
|
||||
$notifications = $request->getStr('notifications');
|
||||
$sounds = $request->getArr('sounds');
|
||||
$theme = $request->getStr('theme');
|
||||
|
||||
$participant->setSettings(array(
|
||||
'notifications' => $notifications,
|
||||
'sounds' => $sounds,
|
||||
'theme' => $theme,
|
||||
));
|
||||
$participant->save();
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($view_uri);
|
||||
}
|
||||
|
||||
$notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY;
|
||||
$notification_default = $viewer->getUserSetting($notification_key);
|
||||
|
||||
$sound_key = PhabricatorConpherenceSoundSetting::SETTINGKEY;
|
||||
$sound_default = $viewer->getUserSetting($sound_key);
|
||||
|
||||
$settings = $participant->getSettings();
|
||||
$notifications = idx($settings, 'notifications', $notification_default);
|
||||
$theme = idx($settings, 'theme', ConpherenceRoomSettings::COLOR_LIGHT);
|
||||
|
||||
$sounds = idx($settings, 'sounds', array());
|
||||
$map = PhabricatorConpherenceSoundSetting::getDefaultSound($sound_default);
|
||||
$receive = idx($sounds,
|
||||
ConpherenceRoomSettings::SOUND_RECEIVE,
|
||||
$map[ConpherenceRoomSettings::SOUND_RECEIVE]);
|
||||
$mention = idx($sounds,
|
||||
ConpherenceRoomSettings::SOUND_MENTION,
|
||||
$map[ConpherenceRoomSettings::SOUND_MENTION]);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendControl(
|
||||
id(new AphrontFormRadioButtonControl())
|
||||
->setLabel(pht('Notify'))
|
||||
->addButton(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL,
|
||||
PhabricatorConpherenceNotificationsSetting::getSettingLabel(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL),
|
||||
'')
|
||||
->addButton(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY,
|
||||
PhabricatorConpherenceNotificationsSetting::getSettingLabel(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY),
|
||||
'')
|
||||
->setName('notifications')
|
||||
->setValue($notifications))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('New Message'))
|
||||
->setName('sounds['.ConpherenceRoomSettings::SOUND_RECEIVE.']')
|
||||
->setOptions(ConpherenceRoomSettings::getDropdownSoundMap())
|
||||
->setValue($receive))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Theme'))
|
||||
->setName('theme')
|
||||
->setOptions(ConpherenceRoomSettings::getThemeMap())
|
||||
->setValue($theme));
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Room Preferences'))
|
||||
->appendForm($form)
|
||||
->addCancelButton($view_uri)
|
||||
->addSubmitButton(pht('Save'));
|
||||
}
|
||||
|
||||
}
|
@@ -24,12 +24,6 @@ final class ConpherenceUpdateController
|
||||
case ConpherenceUpdateActions::METADATA:
|
||||
$needed_capabilities[] = PhabricatorPolicyCapability::CAN_EDIT;
|
||||
break;
|
||||
case ConpherenceUpdateActions::JOIN_ROOM:
|
||||
$needed_capabilities[] = PhabricatorPolicyCapability::CAN_JOIN;
|
||||
break;
|
||||
case ConpherenceUpdateActions::NOTIFICATIONS:
|
||||
$need_participants = true;
|
||||
break;
|
||||
case ConpherenceUpdateActions::LOAD:
|
||||
break;
|
||||
}
|
||||
@@ -64,7 +58,7 @@ final class ConpherenceUpdateController
|
||||
case ConpherenceUpdateActions::JOIN_ROOM:
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(
|
||||
ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => array($user->getPHID())));
|
||||
$delete_draft = true;
|
||||
$message = $request->getStr('text');
|
||||
@@ -98,7 +92,7 @@ final class ConpherenceUpdateController
|
||||
if (!empty($person_phids)) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(
|
||||
ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $person_phids));
|
||||
}
|
||||
break;
|
||||
@@ -111,22 +105,10 @@ final class ConpherenceUpdateController
|
||||
if ($person_phid) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(
|
||||
ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('-' => array($person_phid)));
|
||||
$response_mode = 'go-home';
|
||||
}
|
||||
break;
|
||||
case ConpherenceUpdateActions::NOTIFICATIONS:
|
||||
$notifications = $request->getStr('notifications');
|
||||
$participant = $conpherence->getParticipantIfExists($user->getPHID());
|
||||
if (!$participant) {
|
||||
return id(new Aphront404Response());
|
||||
}
|
||||
$participant->setSettings(array('notifications' => $notifications));
|
||||
$participant->save();
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/'.$conpherence->getMonogram());
|
||||
|
||||
break;
|
||||
case ConpherenceUpdateActions::METADATA:
|
||||
$title = $request->getStr('title');
|
||||
@@ -140,10 +122,12 @@ final class ConpherenceUpdateController
|
||||
$title = $request->getStr('title');
|
||||
$topic = $request->getStr('topic');
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_TITLE)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadTitleTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($title);
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_TOPIC)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadTopicTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($topic);
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
|
||||
@@ -151,9 +135,6 @@ final class ConpherenceUpdateController
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
|
||||
->setNewValue($request->getStr('editPolicy'));
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_JOIN_POLICY)
|
||||
->setNewValue($request->getStr('joinPolicy'));
|
||||
if (!$request->getExists('force_ajax')) {
|
||||
$response_mode = 'redirect';
|
||||
}
|
||||
@@ -221,9 +202,6 @@ final class ConpherenceUpdateController
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case ConpherenceUpdateActions::NOTIFICATIONS:
|
||||
$dialog = $this->renderPreferencesDialog($conpherence);
|
||||
break;
|
||||
case ConpherenceUpdateActions::ADD_PERSON:
|
||||
$dialog = $this->renderAddPersonDialog($conpherence);
|
||||
break;
|
||||
@@ -246,71 +224,6 @@ final class ConpherenceUpdateController
|
||||
|
||||
}
|
||||
|
||||
private function renderPreferencesDialog(
|
||||
ConpherenceThread $conpherence) {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$participant = $conpherence->getParticipantIfExists($user->getPHID());
|
||||
if (!$participant) {
|
||||
$can_join = PhabricatorPolicyFilter::hasCapability(
|
||||
$user,
|
||||
$conpherence,
|
||||
PhabricatorPolicyCapability::CAN_JOIN);
|
||||
if ($can_join) {
|
||||
$text = pht(
|
||||
'Notification settings are available after joining the room.');
|
||||
} else if ($user->isLoggedIn()) {
|
||||
$text = pht(
|
||||
'Notification settings not applicable to rooms you can not join.');
|
||||
} else {
|
||||
$text = pht(
|
||||
'Notification settings are available after logging in and joining '.
|
||||
'the room.');
|
||||
}
|
||||
return id(new AphrontDialogView())
|
||||
->setTitle(pht('Room Preferences'))
|
||||
->appendParagraph($text);
|
||||
}
|
||||
|
||||
$notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY;
|
||||
$notification_default = $user->getUserSetting($notification_key);
|
||||
|
||||
$settings = $participant->getSettings();
|
||||
$notifications = idx(
|
||||
$settings,
|
||||
'notifications',
|
||||
$notification_default);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($user)
|
||||
->setFullWidth(true)
|
||||
->appendControl(
|
||||
id(new AphrontFormRadioButtonControl())
|
||||
->addButton(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL,
|
||||
PhabricatorConpherenceNotificationsSetting::getSettingLabel(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL),
|
||||
'')
|
||||
->addButton(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY,
|
||||
PhabricatorConpherenceNotificationsSetting::getSettingLabel(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY),
|
||||
'')
|
||||
->setName('notifications')
|
||||
->setValue($notifications));
|
||||
|
||||
return id(new AphrontDialogView())
|
||||
->setTitle(pht('Room Preferences'))
|
||||
->addHiddenInput('action', 'notifications')
|
||||
->addHiddenInput(
|
||||
'latest_transaction_id',
|
||||
$request->getInt('latest_transaction_id'))
|
||||
->appendForm($form);
|
||||
|
||||
}
|
||||
|
||||
private function renderAddPersonDialog(
|
||||
ConpherenceThread $conpherence) {
|
||||
|
||||
@@ -371,9 +284,9 @@ final class ConpherenceUpdateController
|
||||
$body[] = pht(
|
||||
'Are you sure you want to leave this room?');
|
||||
} else {
|
||||
$title = pht('Banish User');
|
||||
$title = pht('Remove Participant');
|
||||
$body[] = pht(
|
||||
'Banish %s from the realm?',
|
||||
'Remove %s from this room?',
|
||||
phutil_tag('strong', array(), $removed_user->getUsername()));
|
||||
}
|
||||
|
||||
@@ -383,7 +296,7 @@ final class ConpherenceUpdateController
|
||||
'You will be able to rejoin the room later.');
|
||||
} else {
|
||||
$body[] = pht(
|
||||
'This user will be able to rejoin the room later.');
|
||||
'They will be able to rejoin the room later.');
|
||||
}
|
||||
} else {
|
||||
if ($is_self) {
|
||||
@@ -398,7 +311,7 @@ final class ConpherenceUpdateController
|
||||
}
|
||||
} else {
|
||||
$body[] = pht(
|
||||
'This user will not be able to rejoin the room unless invited '.
|
||||
'They will not be able to rejoin the room unless invited '.
|
||||
'again.');
|
||||
}
|
||||
}
|
||||
@@ -457,12 +370,6 @@ final class ConpherenceUpdateController
|
||||
->setName('editPolicy')
|
||||
->setPolicyObject($conpherence)
|
||||
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
|
||||
->setPolicies($policies))
|
||||
->appendChild(
|
||||
id(new AphrontFormPolicyControl())
|
||||
->setName('joinPolicy')
|
||||
->setPolicyObject($conpherence)
|
||||
->setCapability(PhabricatorPolicyCapability::CAN_JOIN)
|
||||
->setPolicies($policies));
|
||||
|
||||
$view = id(new AphrontDialogView())
|
||||
@@ -487,7 +394,6 @@ final class ConpherenceUpdateController
|
||||
$latest_transaction_id) {
|
||||
|
||||
$need_transactions = false;
|
||||
$need_participant_cache = true;
|
||||
switch ($action) {
|
||||
case ConpherenceUpdateActions::METADATA:
|
||||
case ConpherenceUpdateActions::LOAD:
|
||||
@@ -498,7 +404,6 @@ final class ConpherenceUpdateController
|
||||
$need_transactions = true;
|
||||
break;
|
||||
case ConpherenceUpdateActions::REMOVE_PERSON:
|
||||
case ConpherenceUpdateActions::NOTIFICATIONS:
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -508,13 +413,14 @@ final class ConpherenceUpdateController
|
||||
->setViewer($user)
|
||||
->setAfterTransactionID($latest_transaction_id)
|
||||
->needProfileImage(true)
|
||||
->needParticipantCache($need_participant_cache)
|
||||
->needParticipants(true)
|
||||
->needTransactions($need_transactions)
|
||||
->withIDs(array($conpherence_id))
|
||||
->executeOne();
|
||||
|
||||
$non_update = false;
|
||||
$participant = $conpherence->getParticipant($user->getPHID());
|
||||
|
||||
if ($need_transactions && $conpherence->getTransactions()) {
|
||||
$data = ConpherenceTransactionRenderer::renderTransactions(
|
||||
$user,
|
||||
@@ -522,9 +428,7 @@ final class ConpherenceUpdateController
|
||||
$key = PhabricatorConpherenceColumnMinimizeSetting::SETTINGKEY;
|
||||
$minimized = $user->getUserSetting($key);
|
||||
if (!$minimized) {
|
||||
$participant_obj = $conpherence->getParticipant($user->getPHID());
|
||||
$participant_obj
|
||||
->markUpToDate($conpherence, $data['latest_transaction']);
|
||||
$participant->markUpToDate($conpherence);
|
||||
}
|
||||
} else if ($need_transactions) {
|
||||
$non_update = true;
|
||||
@@ -541,18 +445,12 @@ final class ConpherenceUpdateController
|
||||
$people_widget = null;
|
||||
switch ($action) {
|
||||
case ConpherenceUpdateActions::METADATA:
|
||||
$policy_objects = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($user)
|
||||
->setObject($conpherence)
|
||||
->execute();
|
||||
$header = $this->buildHeaderPaneContent(
|
||||
$conpherence,
|
||||
$policy_objects);
|
||||
$header = $this->buildHeaderPaneContent($conpherence);
|
||||
$header = hsprintf('%s', $header);
|
||||
$nav_item = id(new ConpherenceThreadListView())
|
||||
->setUser($user)
|
||||
->setBaseURI($this->getApplicationURI())
|
||||
->renderSingleThread($conpherence, $policy_objects);
|
||||
->renderThreadItem($conpherence);
|
||||
$nav_item = hsprintf('%s', $nav_item);
|
||||
break;
|
||||
case ConpherenceUpdateActions::ADD_PERSON:
|
||||
@@ -563,7 +461,6 @@ final class ConpherenceUpdateController
|
||||
$people_widget = hsprintf('%s', $people_widget->render());
|
||||
break;
|
||||
case ConpherenceUpdateActions::REMOVE_PERSON:
|
||||
case ConpherenceUpdateActions::NOTIFICATIONS:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -571,6 +468,11 @@ final class ConpherenceUpdateController
|
||||
$dropdown_query = id(new AphlictDropdownDataQuery())
|
||||
->setViewer($user);
|
||||
$dropdown_query->execute();
|
||||
|
||||
$sounds = $this->getSoundForParticipant($user, $participant);
|
||||
$receive_sound = $sounds[ConpherenceRoomSettings::SOUND_RECEIVE];
|
||||
$mention_sound = $sounds[ConpherenceRoomSettings::SOUND_MENTION];
|
||||
|
||||
$content = array(
|
||||
'non_update' => $non_update,
|
||||
'transactions' => hsprintf('%s', $rendered_transactions),
|
||||
@@ -584,9 +486,40 @@ final class ConpherenceUpdateController
|
||||
$dropdown_query->getNotificationData(),
|
||||
$dropdown_query->getConpherenceData(),
|
||||
),
|
||||
'sound' => array(
|
||||
'receive' => $receive_sound,
|
||||
'mention' => $mention_sound,
|
||||
),
|
||||
);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function getSoundForParticipant(
|
||||
PhabricatorUser $user,
|
||||
ConpherenceParticipant $participant) {
|
||||
|
||||
$sound_key = PhabricatorConpherenceSoundSetting::SETTINGKEY;
|
||||
$sound_default = $user->getUserSetting($sound_key);
|
||||
|
||||
$settings = $participant->getSettings();
|
||||
$sounds = idx($settings, 'sounds', array());
|
||||
$map = PhabricatorConpherenceSoundSetting::getDefaultSound($sound_default);
|
||||
|
||||
$receive = idx($sounds,
|
||||
ConpherenceRoomSettings::SOUND_RECEIVE,
|
||||
$map[ConpherenceRoomSettings::SOUND_RECEIVE]);
|
||||
$mention = idx($sounds,
|
||||
ConpherenceRoomSettings::SOUND_MENTION,
|
||||
$map[ConpherenceRoomSettings::SOUND_MENTION]);
|
||||
|
||||
$sound_map = ConpherenceRoomSettings::getSoundMap();
|
||||
|
||||
return array(
|
||||
ConpherenceRoomSettings::SOUND_RECEIVE => $sound_map[$receive]['rsrc'],
|
||||
ConpherenceRoomSettings::SOUND_MENTION => $sound_map[$mention]['rsrc'],
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -20,7 +20,6 @@ final class ConpherenceViewController extends
|
||||
->setViewer($user)
|
||||
->withIDs(array($conpherence_id))
|
||||
->needProfileImage(true)
|
||||
->needParticipantCache(true)
|
||||
->needTransactions(true)
|
||||
->setTransactionLimit($this->getMainQueryLimit());
|
||||
|
||||
@@ -56,15 +55,15 @@ final class ConpherenceViewController extends
|
||||
}
|
||||
$this->setConpherence($conpherence);
|
||||
|
||||
$transactions = $this->getNeededTransactions(
|
||||
$conpherence,
|
||||
$old_message_id);
|
||||
$latest_transaction = head($transactions);
|
||||
$participant = $conpherence->getParticipantIfExists($user->getPHID());
|
||||
$theme = ConpherenceRoomSettings::COLOR_LIGHT;
|
||||
|
||||
if ($participant) {
|
||||
$settings = $participant->getSettings();
|
||||
$theme = idx($settings, 'theme', ConpherenceRoomSettings::COLOR_LIGHT);
|
||||
if (!$participant->isUpToDate($conpherence)) {
|
||||
$write_guard = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$participant->markUpToDate($conpherence, $latest_transaction);
|
||||
$participant->markUpToDate($conpherence);
|
||||
$user->clearCacheData(PhabricatorUserMessageCountCacheType::KEY_COUNT);
|
||||
unset($write_guard);
|
||||
}
|
||||
@@ -83,11 +82,7 @@ final class ConpherenceViewController extends
|
||||
$form = null;
|
||||
$content = array('transactions' => $messages);
|
||||
} else {
|
||||
$policy_objects = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($user)
|
||||
->setObject($conpherence)
|
||||
->execute();
|
||||
$header = $this->buildHeaderPaneContent($conpherence, $policy_objects);
|
||||
$header = $this->buildHeaderPaneContent($conpherence);
|
||||
$search = $this->buildSearchForm();
|
||||
$form = $this->renderFormContent();
|
||||
$content = array(
|
||||
@@ -119,11 +114,6 @@ final class ConpherenceViewController extends
|
||||
return id(new AphrontAjaxResponse())->setContent($content);
|
||||
}
|
||||
|
||||
$can_join = PhabricatorPolicyFilter::hasCapability(
|
||||
$user,
|
||||
$conpherence,
|
||||
PhabricatorPolicyCapability::CAN_JOIN);
|
||||
|
||||
$layout = id(new ConpherenceLayoutView())
|
||||
->setUser($user)
|
||||
->setBaseURI($this->getApplicationURI())
|
||||
@@ -132,6 +122,7 @@ final class ConpherenceViewController extends
|
||||
->setSearch($search)
|
||||
->setMessages($messages)
|
||||
->setReplyForm($form)
|
||||
->setTheme($theme)
|
||||
->setLatestTransactionID($data['latest_transaction_id'])
|
||||
->setRole('thread');
|
||||
|
||||
@@ -151,14 +142,8 @@ final class ConpherenceViewController extends
|
||||
|
||||
$conpherence = $this->getConpherence();
|
||||
$user = $this->getRequest()->getUser();
|
||||
$can_join = PhabricatorPolicyFilter::hasCapability(
|
||||
$user,
|
||||
$conpherence,
|
||||
PhabricatorPolicyCapability::CAN_JOIN);
|
||||
|
||||
$participating = $conpherence->getParticipantIfExists($user->getPHID());
|
||||
if (!$can_join && !$participating && $user->isLoggedIn()) {
|
||||
return null;
|
||||
}
|
||||
$draft = PhabricatorDraft::newFromUserAndKey(
|
||||
$user,
|
||||
$conpherence->getPHID());
|
||||
@@ -184,6 +169,7 @@ final class ConpherenceViewController extends
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setUser($user)
|
||||
->setName('text')
|
||||
->setSendOnEnter(true)
|
||||
->setValue($draft->getDraft()));
|
||||
|
||||
$status_view = phutil_tag(
|
||||
@@ -214,33 +200,6 @@ final class ConpherenceViewController extends
|
||||
}
|
||||
}
|
||||
|
||||
private function getNeededTransactions(
|
||||
ConpherenceThread $conpherence,
|
||||
$message_id) {
|
||||
|
||||
if ($message_id) {
|
||||
$newer_transactions = $conpherence->getTransactions();
|
||||
$query = id(new ConpherenceTransactionQuery())
|
||||
->setViewer($this->getRequest()->getUser())
|
||||
->withObjectPHIDs(array($conpherence->getPHID()))
|
||||
->setAfterID($message_id)
|
||||
->needHandles(true)
|
||||
->setLimit(self::OLDER_FETCH_LIMIT);
|
||||
$older_transactions = $query->execute();
|
||||
$handles = array();
|
||||
foreach ($older_transactions as $transaction) {
|
||||
$handles += $transaction->getHandles();
|
||||
}
|
||||
$conpherence->attachHandles($conpherence->getHandles() + $handles);
|
||||
$transactions = array_merge($newer_transactions, $older_transactions);
|
||||
$conpherence->attachTransactions($transactions);
|
||||
} else {
|
||||
$transactions = $conpherence->getTransactions();
|
||||
}
|
||||
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
private function getMainQueryLimit() {
|
||||
$request = $this->getRequest();
|
||||
$base_limit = ConpherenceThreadQuery::TRANSACTION_LIMIT;
|
||||
|
@@ -37,16 +37,19 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||
if (!$errors) {
|
||||
$xactions = array();
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $participant_phids));
|
||||
if ($title) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_TITLE)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadTitleTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($title);
|
||||
}
|
||||
if (strlen($topic)) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_TOPIC)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadTopicTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($topic);
|
||||
}
|
||||
|
||||
@@ -86,49 +89,14 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
||||
|
||||
$types[] = ConpherenceTransaction::TYPE_TITLE;
|
||||
$types[] = ConpherenceTransaction::TYPE_TOPIC;
|
||||
$types[] = ConpherenceTransaction::TYPE_PARTICIPANTS;
|
||||
$types[] = ConpherenceTransaction::TYPE_PICTURE;
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_JOIN_POLICY;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
protected function getCustomTransactionOldValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_TITLE:
|
||||
return $object->getTitle();
|
||||
case ConpherenceTransaction::TYPE_TOPIC:
|
||||
return $object->getTopic();
|
||||
case ConpherenceTransaction::TYPE_PICTURE:
|
||||
return $object->getProfileImagePHID();
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
if ($this->getIsNewObject()) {
|
||||
return array();
|
||||
}
|
||||
return $object->getParticipantPHIDs();
|
||||
}
|
||||
}
|
||||
|
||||
protected function getCustomTransactionNewValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_TITLE:
|
||||
case ConpherenceTransaction::TYPE_TOPIC:
|
||||
case ConpherenceTransaction::TYPE_PICTURE:
|
||||
return $xaction->getNewValue();
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
return $this->getPHIDTransactionNewValue($xaction);
|
||||
}
|
||||
public function getCreateObjectTitle($author, $object) {
|
||||
return pht('%s created this room.', $author);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,109 +118,6 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||
return $lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to apply initial effects IFF the conpherence is new. We must
|
||||
* save the conpherence first thing to make sure we have an id and a phid, as
|
||||
* well as create the initial set of participants so that we pass policy
|
||||
* checks.
|
||||
*/
|
||||
protected function shouldApplyInitialEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
return $this->getIsNewObject();
|
||||
}
|
||||
|
||||
protected function applyInitialEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
$object->save();
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
// Since this is a new ConpherenceThread, we have to create the
|
||||
// participation data asap to pass policy checks. For existing
|
||||
// ConpherenceThreads, the existing participation is correct
|
||||
// at this stage. Note that later in applyCustomExternalTransaction
|
||||
// this participation data will be updated, particularly the
|
||||
// behindTransactionPHID which is just a generated dummy for now.
|
||||
$participants = array();
|
||||
$phids = $this->getPHIDTransactionNewValue($xaction, array());
|
||||
foreach ($phids as $phid) {
|
||||
if ($phid == $this->getActor()->getPHID()) {
|
||||
$status = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||
$message_count = 1;
|
||||
} else {
|
||||
$status = ConpherenceParticipationStatus::BEHIND;
|
||||
$message_count = 0;
|
||||
}
|
||||
$participants[$phid] =
|
||||
id(new ConpherenceParticipant())
|
||||
->setConpherencePHID($object->getPHID())
|
||||
->setParticipantPHID($phid)
|
||||
->setParticipationStatus($status)
|
||||
->setDateTouched(time())
|
||||
->setBehindTransactionPHID($xaction->generatePHID())
|
||||
->setSeenMessageCount($message_count)
|
||||
->save();
|
||||
$object->attachParticipants($participants);
|
||||
$object->setRecentParticipantPHIDs(array_keys($participants));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyCustomInternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
$make_author_recent_participant = true;
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_TITLE:
|
||||
$object->setTitle($xaction->getNewValue());
|
||||
break;
|
||||
case ConpherenceTransaction::TYPE_TOPIC:
|
||||
$object->setTopic($xaction->getNewValue());
|
||||
break;
|
||||
case ConpherenceTransaction::TYPE_PICTURE:
|
||||
$object->setProfileImagePHID($xaction->getNewValue());
|
||||
break;
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
if (!$this->getIsNewObject()) {
|
||||
$old_map = array_fuse($xaction->getOldValue());
|
||||
$new_map = array_fuse($xaction->getNewValue());
|
||||
// if we added people, add them to the end of "recent" participants
|
||||
$add = array_keys(array_diff_key($new_map, $old_map));
|
||||
// if we remove people, then definintely remove them from "recent"
|
||||
// participants
|
||||
$del = array_keys(array_diff_key($old_map, $new_map));
|
||||
if ($add || $del) {
|
||||
$participants = $object->getRecentParticipantPHIDs();
|
||||
if ($add) {
|
||||
$participants = array_merge($participants, $add);
|
||||
}
|
||||
if ($del) {
|
||||
$participants = array_diff($participants, $del);
|
||||
$actor = $this->requireActor();
|
||||
if (in_array($actor->getPHID(), $del)) {
|
||||
$make_author_recent_participant = false;
|
||||
}
|
||||
}
|
||||
$participants = array_slice(array_unique($participants), 0, 10);
|
||||
$object->setRecentParticipantPHIDs($participants);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ($make_author_recent_participant) {
|
||||
$this->makeAuthorMostRecentParticipant($object, $xaction);
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyBuiltinInternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
@@ -266,108 +131,24 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||
return parent::applyBuiltinInternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
private function makeAuthorMostRecentParticipant(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
$participants = $object->getRecentParticipantPHIDs();
|
||||
array_unshift($participants, $xaction->getAuthorPHID());
|
||||
$participants = array_slice(array_unique($participants), 0, 10);
|
||||
|
||||
$object->setRecentParticipantPHIDs($participants);
|
||||
}
|
||||
|
||||
protected function applyCustomExternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
if ($this->getIsNewObject()) {
|
||||
continue;
|
||||
}
|
||||
$participants = $object->getParticipants();
|
||||
|
||||
$old_map = array_fuse($xaction->getOldValue());
|
||||
$new_map = array_fuse($xaction->getNewValue());
|
||||
|
||||
$remove = array_keys(array_diff_key($old_map, $new_map));
|
||||
foreach ($remove as $phid) {
|
||||
$remove_participant = $participants[$phid];
|
||||
$remove_participant->delete();
|
||||
unset($participants[$phid]);
|
||||
}
|
||||
|
||||
$add = array_keys(array_diff_key($new_map, $old_map));
|
||||
foreach ($add as $phid) {
|
||||
if ($phid == $this->getActor()->getPHID()) {
|
||||
$status = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||
$message_count = $object->getMessageCount();
|
||||
} else {
|
||||
$status = ConpherenceParticipationStatus::BEHIND;
|
||||
$message_count = 0;
|
||||
}
|
||||
$participants[$phid] =
|
||||
id(new ConpherenceParticipant())
|
||||
->setConpherencePHID($object->getPHID())
|
||||
->setParticipantPHID($phid)
|
||||
->setParticipationStatus($status)
|
||||
->setDateTouched(time())
|
||||
->setBehindTransactionPHID($xaction->getPHID())
|
||||
->setSeenMessageCount($message_count)
|
||||
->save();
|
||||
}
|
||||
$object->attachParticipants($participants);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyFinalEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
if (!$xactions) {
|
||||
return $xactions;
|
||||
}
|
||||
|
||||
$message_count = 0;
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorTransactions::TYPE_COMMENT:
|
||||
$message_count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// update everyone's participation status on the last xaction -only-
|
||||
$xaction = end($xactions);
|
||||
$xaction_phid = $xaction->getPHID();
|
||||
$behind = ConpherenceParticipationStatus::BEHIND;
|
||||
$up_to_date = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||
$acting_phid = $this->getActingAsPHID();
|
||||
$participants = $object->getParticipants();
|
||||
$user = $this->getActor();
|
||||
$time = time();
|
||||
foreach ($participants as $phid => $participant) {
|
||||
if ($phid != $user->getPHID()) {
|
||||
if ($participant->getParticipationStatus() != $behind) {
|
||||
$participant->setBehindTransactionPHID($xaction_phid);
|
||||
$participant->setSeenMessageCount(
|
||||
$object->getMessageCount() - $message_count);
|
||||
}
|
||||
$participant->setParticipationStatus($behind);
|
||||
$participant->setDateTouched($time);
|
||||
} else {
|
||||
$participant->setSeenMessageCount($object->getMessageCount());
|
||||
$participant->setBehindTransactionPHID($xaction_phid);
|
||||
$participant->setParticipationStatus($up_to_date);
|
||||
$participant->setDateTouched($time);
|
||||
foreach ($participants as $participant) {
|
||||
if ($participant->getParticipantPHID() == $acting_phid) {
|
||||
$participant->markUpToDate($object);
|
||||
}
|
||||
$participant->save();
|
||||
}
|
||||
|
||||
PhabricatorUserCache::clearCaches(
|
||||
PhabricatorUserMessageCountCacheType::KEY_COUNT,
|
||||
array_keys($participants));
|
||||
if ($participants) {
|
||||
PhabricatorUserCache::clearCaches(
|
||||
PhabricatorUserMessageCountCacheType::KEY_COUNT,
|
||||
array_keys($participants));
|
||||
}
|
||||
|
||||
if ($xactions) {
|
||||
$data = array(
|
||||
@@ -390,36 +171,34 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||
parent::requireCapabilities($object, $xaction);
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
case ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE:
|
||||
$old_map = array_fuse($xaction->getOldValue());
|
||||
$new_map = array_fuse($xaction->getNewValue());
|
||||
|
||||
$add = array_keys(array_diff_key($new_map, $old_map));
|
||||
$rem = array_keys(array_diff_key($old_map, $new_map));
|
||||
|
||||
$actor_phid = $this->requireActor()->getPHID();
|
||||
$actor_phid = $this->getActingAsPHID();
|
||||
|
||||
$is_join = (($add === array($actor_phid)) && !$rem);
|
||||
$is_leave = (($rem === array($actor_phid)) && !$add);
|
||||
|
||||
if ($is_join) {
|
||||
// You need CAN_JOIN to join a room.
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$this->requireActor(),
|
||||
$object,
|
||||
PhabricatorPolicyCapability::CAN_JOIN);
|
||||
// Anyone can join a thread they can see.
|
||||
} else if ($is_leave) {
|
||||
// You don't need any capabilities to leave a conpherence thread.
|
||||
// Anyone can leave a thread.
|
||||
} else {
|
||||
// You need CAN_EDIT to change participants other than yourself.
|
||||
// You need CAN_EDIT to add or remove participants. For additional
|
||||
// discussion, see D17696 and T4411.
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$this->requireActor(),
|
||||
$object,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
}
|
||||
break;
|
||||
case ConpherenceTransaction::TYPE_TITLE:
|
||||
case ConpherenceTransaction::TYPE_TOPIC:
|
||||
|
||||
case ConpherenceThreadTitleTransaction::TRANSACTIONTYPE:
|
||||
case ConpherenceThreadTopicTransaction::TRANSACTIONTYPE:
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$this->requireActor(),
|
||||
$object,
|
||||
@@ -428,21 +207,6 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||
}
|
||||
}
|
||||
|
||||
protected function mergeTransactions(
|
||||
PhabricatorApplicationTransaction $u,
|
||||
PhabricatorApplicationTransaction $v) {
|
||||
|
||||
$type = $u->getTransactionType();
|
||||
switch ($type) {
|
||||
case ConpherenceTransaction::TYPE_TITLE:
|
||||
return $v;
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
return $this->mergePHIDOrEdgeTransactions($u, $v);
|
||||
}
|
||||
|
||||
return parent::mergeTransactions($u, $v);
|
||||
}
|
||||
|
||||
protected function shouldSendMail(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
@@ -541,84 +305,8 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||
return PhabricatorEnv::getEnvConfig('metamta.conpherence.subject-prefix');
|
||||
}
|
||||
|
||||
protected function shouldPublishFeedStory(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_TITLE:
|
||||
case ConpherenceTransaction::TYPE_TOPIC:
|
||||
case ConpherenceTransaction::TYPE_PICTURE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function supportsSearch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
$type,
|
||||
array $xactions) {
|
||||
|
||||
$errors = parent::validateTransaction($object, $type, $xactions);
|
||||
|
||||
switch ($type) {
|
||||
case ConpherenceTransaction::TYPE_TITLE:
|
||||
if (empty($xactions)) {
|
||||
break;
|
||||
}
|
||||
$missing = $this->validateIsEmptyTextField(
|
||||
$object->getTitle(),
|
||||
$xactions);
|
||||
|
||||
if ($missing) {
|
||||
$detail = pht('Room title is required.');
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Required'),
|
||||
$detail,
|
||||
last($xactions));
|
||||
|
||||
$error->setIsMissingFieldError(true);
|
||||
$errors[] = $error;
|
||||
}
|
||||
break;
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
foreach ($xactions as $xaction) {
|
||||
$new_phids = $this->getPHIDTransactionNewValue($xaction, array());
|
||||
$old_phids = nonempty($object->getParticipantPHIDs(), array());
|
||||
$phids = array_diff($new_phids, $old_phids);
|
||||
|
||||
if (!$phids) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$users = id(new PhabricatorPeopleQuery())
|
||||
->setViewer($this->requireActor())
|
||||
->withPHIDs($phids)
|
||||
->execute();
|
||||
$users = mpull($users, null, 'getPHID');
|
||||
foreach ($phids as $phid) {
|
||||
if (isset($users[$phid])) {
|
||||
continue;
|
||||
}
|
||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht('New room participant "%s" is not a valid user.', $phid),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
@@ -55,7 +55,8 @@ final class ConpherenceReplyHandler extends PhabricatorMailReplyHandler {
|
||||
$xactions = array();
|
||||
if ($this->getMailAddedParticipantPHIDs()) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $this->getMailAddedParticipantPHIDs()));
|
||||
}
|
||||
|
||||
|
@@ -56,9 +56,9 @@ final class ConpherenceFulltextQuery
|
||||
}
|
||||
|
||||
if (strlen($this->fulltext)) {
|
||||
$compiled_query = PhabricatorSearchDocument::newQueryCompiler()
|
||||
->setQuery($this->fulltext)
|
||||
->compileQuery();
|
||||
$compiler = PhabricatorSearchDocument::newQueryCompiler();
|
||||
$tokens = $compiler->newTokens($this->fulltext);
|
||||
$compiled_query = $compiler->compileQuery($tokens);
|
||||
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
|
@@ -1,73 +1,69 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Query class that answers the question:
|
||||
*
|
||||
* - Q: How many unread conpherences am I participating in?
|
||||
* - A:
|
||||
* id(new ConpherenceParticipantCountQuery())
|
||||
* ->withParticipantPHIDs(array($my_phid))
|
||||
* ->withParticipationStatus(ConpherenceParticipationStatus::BEHIND)
|
||||
* ->execute();
|
||||
*/
|
||||
final class ConpherenceParticipantCountQuery
|
||||
extends PhabricatorOffsetPagedQuery {
|
||||
|
||||
private $participantPHIDs;
|
||||
private $participationStatus;
|
||||
private $unread;
|
||||
|
||||
public function withParticipantPHIDs(array $phids) {
|
||||
$this->participantPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withParticipationStatus($participation_status) {
|
||||
$this->participationStatus = $participation_status;
|
||||
public function withUnread($unread) {
|
||||
$this->unread = $unread;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$thread = new ConpherenceThread();
|
||||
$table = new ConpherenceParticipant();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
$conn = $table->establishConnection('r');
|
||||
|
||||
$rows = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT COUNT(*) as count, participantPHID '.
|
||||
'FROM %T participant %Q %Q %Q',
|
||||
$conn,
|
||||
'SELECT COUNT(*) as count, participantPHID
|
||||
FROM %T participant JOIN %T thread
|
||||
ON participant.conpherencePHID = thread.phid %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildGroupByClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
$thread->getTableName(),
|
||||
$this->buildWhereClause($conn),
|
||||
$this->buildGroupByClause($conn),
|
||||
$this->buildLimitClause($conn));
|
||||
|
||||
return ipull($rows, 'count', 'participantPHID');
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
|
||||
$where = array();
|
||||
|
||||
if ($this->participantPHIDs) {
|
||||
if ($this->participantPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'participantPHID IN (%Ls)',
|
||||
$conn,
|
||||
'participant.participantPHID IN (%Ls)',
|
||||
$this->participantPHIDs);
|
||||
}
|
||||
|
||||
if ($this->participationStatus !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'participationStatus = %d',
|
||||
$this->participationStatus);
|
||||
if ($this->unread !== null) {
|
||||
if ($this->unread) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'participant.seenMessageCount < thread.messageCount');
|
||||
} else {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'participant.seenMessageCount >= thread.messageCount');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
private function buildGroupByClause(AphrontDatabaseConnection $conn_r) {
|
||||
$group_by = qsprintf(
|
||||
$conn_r,
|
||||
private function buildGroupByClause(AphrontDatabaseConnection $conn) {
|
||||
return qsprintf(
|
||||
$conn,
|
||||
'GROUP BY participantPHID');
|
||||
|
||||
return $group_by;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,128 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Query class that answers these questions:
|
||||
*
|
||||
* - Q: What are the conpherences to show when I land on /conpherence/ ?
|
||||
* - A:
|
||||
*
|
||||
* id(new ConpherenceParticipantQuery())
|
||||
* ->withParticipantPHIDs(array($my_phid))
|
||||
* ->execute();
|
||||
*
|
||||
* - Q: What are the next set of conpherences as I scroll up (more recent) or
|
||||
* down (less recent) this list of conpherences?
|
||||
* - A:
|
||||
*
|
||||
* id(new ConpherenceParticipantQuery())
|
||||
* ->withParticipantPHIDs(array($my_phid))
|
||||
* ->withParticipantCursor($top_participant)
|
||||
* ->setOrder(ConpherenceParticipantQuery::ORDER_NEWER)
|
||||
* ->execute();
|
||||
*
|
||||
* -or-
|
||||
*
|
||||
* id(new ConpherenceParticipantQuery())
|
||||
* ->withParticipantPHIDs(array($my_phid))
|
||||
* ->withParticipantCursor($bottom_participant)
|
||||
* ->setOrder(ConpherenceParticipantQuery::ORDER_OLDER)
|
||||
* ->execute();
|
||||
*
|
||||
* For counts of read, un-read, or all conpherences by participant, see
|
||||
* @{class:ConpherenceParticipantCountQuery}.
|
||||
*/
|
||||
final class ConpherenceParticipantQuery extends PhabricatorOffsetPagedQuery {
|
||||
|
||||
const LIMIT = 100;
|
||||
const ORDER_NEWER = 'newer';
|
||||
const ORDER_OLDER = 'older';
|
||||
|
||||
private $participantPHIDs;
|
||||
private $participantCursor;
|
||||
private $order = self::ORDER_OLDER;
|
||||
|
||||
public function withParticipantPHIDs(array $phids) {
|
||||
$this->participantPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withParticipantCursor(ConpherenceParticipant $participant) {
|
||||
$this->participantCursor = $participant;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setOrder($order) {
|
||||
$this->order = $order;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$table = new ConpherenceParticipant();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
$thread = new ConpherenceThread();
|
||||
|
||||
$conn = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T participant %Q %Q %Q',
|
||||
$conn,
|
||||
'SELECT * FROM %T participant JOIN %T thread
|
||||
ON participant.conpherencePHID = thread.phid %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
$thread->getTableName(),
|
||||
$this->buildWhereClause($conn),
|
||||
$this->buildOrderClause($conn),
|
||||
$this->buildLimitClause($conn));
|
||||
|
||||
$participants = $table->loadAllFromArray($data);
|
||||
|
||||
$participants = mpull($participants, null, 'getConpherencePHID');
|
||||
|
||||
if ($this->order == self::ORDER_NEWER) {
|
||||
$participants = array_reverse($participants);
|
||||
}
|
||||
|
||||
return $participants;
|
||||
return $table->loadAllFromArray($data);
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
|
||||
$where = array();
|
||||
|
||||
if ($this->participantPHIDs) {
|
||||
if ($this->participantPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'participantPHID IN (%Ls)',
|
||||
$this->participantPHIDs);
|
||||
}
|
||||
|
||||
if ($this->participantCursor) {
|
||||
$date_touched = $this->participantCursor->getDateTouched();
|
||||
$id = $this->participantCursor->getID();
|
||||
if ($this->order == self::ORDER_OLDER) {
|
||||
$compare_date = '<';
|
||||
$compare_id = '<=';
|
||||
} else {
|
||||
$compare_date = '>';
|
||||
$compare_id = '>=';
|
||||
}
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'(dateTouched %Q %d OR (dateTouched = %d AND id %Q %d))',
|
||||
$compare_date,
|
||||
$date_touched,
|
||||
$date_touched,
|
||||
$compare_id,
|
||||
$id);
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
private function buildOrderClause(AphrontDatabaseConnection $conn_r) {
|
||||
$order_word = ($this->order == self::ORDER_OLDER) ? 'DESC' : 'ASC';
|
||||
// if these are different direction we won't get as efficient a query
|
||||
// see http://dev.mysql.com/doc/refman/5.5/en/order-by-optimization.html
|
||||
$order = qsprintf(
|
||||
$conn_r,
|
||||
'ORDER BY dateTouched %Q, id %Q',
|
||||
$order_word,
|
||||
$order_word);
|
||||
|
||||
return $order;
|
||||
private function buildOrderClause(AphrontDatabaseConnection $conn) {
|
||||
return qsprintf(
|
||||
$conn,
|
||||
'ORDER BY thread.dateModified DESC, thread.id DESC, participant.id DESC');
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -10,18 +10,12 @@ final class ConpherenceThreadQuery
|
||||
private $participantPHIDs;
|
||||
private $needParticipants;
|
||||
private $needTransactions;
|
||||
private $needParticipantCache;
|
||||
private $afterTransactionID;
|
||||
private $beforeTransactionID;
|
||||
private $transactionLimit;
|
||||
private $fulltext;
|
||||
private $needProfileImage;
|
||||
|
||||
public function needParticipantCache($participant_cache) {
|
||||
$this->needParticipantCache = $participant_cache;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function needParticipants($need) {
|
||||
$this->needParticipants = $need;
|
||||
return $this;
|
||||
@@ -101,9 +95,6 @@ final class ConpherenceThreadQuery
|
||||
if ($conpherences) {
|
||||
$conpherences = mpull($conpherences, null, 'getPHID');
|
||||
$this->loadParticipantsAndInitHandles($conpherences);
|
||||
if ($this->needParticipantCache) {
|
||||
$this->loadCoreHandles($conpherences, 'getRecentParticipantPHIDs');
|
||||
}
|
||||
if ($this->needParticipants) {
|
||||
$this->loadCoreHandles($conpherences, 'getParticipantPHIDs');
|
||||
}
|
||||
|