Merge branch 'github-facebook-master' into blender-tweaks

This commit is contained in:
2017-04-27 16:14:16 +02:00
537 changed files with 12567 additions and 17504 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 993 B

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

File diff suppressed because it is too large Load Diff

View File

@@ -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',

View File

@@ -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.

View File

@@ -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();

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
ADD lastActionDiffPHID VARBINARY(64);

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
ADD lastCommentDiffPHID VARBINARY(64);

View 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']);
}

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
ADD lastActorPHID VARBINARY(64);

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
ADD voidedPHID VARBINARY(64);

View 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};

View 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);
}

View File

@@ -0,0 +1,2 @@
DELETE FROM {$NAMESPACE}_conpherence.conpherence_transaction
WHERE transactionType = 'picture-crop';

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_thread
DROP COLUMN recentParticipantPHIDs;

View 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};

View File

@@ -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};

View 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};

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_file.file
ADD isDeleted BOOL NOT NULL DEFAULT 0;

View 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};

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant
DROP behindTransactionPHID;

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant
DROP participationStatus;

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant
DROP dateTouched;

View 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()));
}

View File

@@ -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']);
}

File diff suppressed because one or more lines are too long

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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) {

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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(

View File

@@ -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();

View File

@@ -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);

View File

@@ -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(),
));

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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));
}

View File

@@ -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);
}

View File

@@ -474,7 +474,7 @@ abstract class PhabricatorAuthProvider extends Phobject {
true);
}
return PhabricatorHash::digest($phcid);
return PhabricatorHash::weakDigest($phcid);
}
protected function verifyAuthCSRFCode(AphrontRequest $request, $actual) {

View File

@@ -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;

View File

@@ -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,

View 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();
}
}

View File

@@ -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(

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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)));
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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'),

View File

@@ -226,7 +226,7 @@ final class PhabricatorCalendarEventQuery
$set = $event->newRecurrenceSet();
$recurrences = $set->getEventsBetween(
null,
$start_date,
$end_date,
$limit + 1);

View File

@@ -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();
}

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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',
),
),
);

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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',
));

View File

@@ -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;
}
}

View File

@@ -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.'/',

View File

@@ -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));
}
}
}
}

View File

@@ -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(

View File

@@ -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,
),
),
)),
);
}

View File

@@ -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(

View File

@@ -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'")),

View File

@@ -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));
}

View File

@@ -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)

View File

@@ -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())

View File

@@ -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())

View File

@@ -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',
),

View File

@@ -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),
);
}

View File

@@ -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) {

View File

@@ -1,8 +0,0 @@
<?php
final class ConpherenceParticipationStatus extends ConpherenceConstants {
const UP_TO_DATE = 0;
const BEHIND = 1;
}

View File

@@ -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;
}
}

View File

@@ -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';
}

View File

@@ -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(

View File

@@ -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(

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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())

View File

@@ -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'));
}
}

View File

@@ -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'],
);
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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()));
}

View File

@@ -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,

View File

@@ -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;
}
}

View File

@@ -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');
}
}

View File

@@ -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');
}

Some files were not shown because too many files have changed in this diff Show More