@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix a bug where "View as Query" could replace a saved query row by ID, causing workboard 404s

Summary:
Fixes T13208. See that task for details.

The `clone $query` line is safe if `$query` is a builtin query (like "open").

However, if it's a saved query we clone not only the query parameters but the ID, too. Then when we `save()` the query later, we overwrite the original query.

So this would happen in the database. First, you run a query and save it as the workboard default (query key "abc123"):

| 123 | abc123 | {"...xxx..."} |

Then we `clone` it and change the parameters, and `save()` it. But that causes an `UPDATE ... WHERE id = 123` and the table now looks like this:

| 123 | def456 | {"...yyy..."} |

What we want is to create a new query instead, with an `INSERT ...`:

| 123 | abc123 | {"...xxx..."} |
| 124 | def456 | {"...yyy..."} |

Test Plan:
- Followed reproduction steps from above.
- With just the new `save()` guard, hit the guard error.
- With the `newCopy()`, got a new copy of the query and "View as Query" remained functional without overwriting the original query row.
- Ran migration, saw an affected board get fixed.

Reviewers: amckinley, joshuaspence

Reviewed By: joshuaspence

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T13208

Differential Revision: https://secure.phabricator.com/D19768

+66 -1
+50
resources/sql/autopatches/20181031.board.01.queryreset.php
··· 1 + <?php 2 + 3 + // See T13208. It was previously possible to replace a saved query with another 4 + // saved query, causing loss of the first query. Find projects which have their 5 + // default query set to an invalid query and throw the setting away. 6 + 7 + $viewer = PhabricatorUser::getOmnipotentUser(); 8 + 9 + $table = new PhabricatorProject(); 10 + $conn = $table->establishConnection('w'); 11 + 12 + $iterator = new LiskMigrationIterator($table); 13 + $search_engine = id(new ManiphestTaskSearchEngine()) 14 + ->setViewer($viewer); 15 + 16 + foreach ($iterator as $project) { 17 + $default_filter = $project->getDefaultWorkboardFilter(); 18 + if (!strlen($default_filter)) { 19 + continue; 20 + } 21 + 22 + if ($search_engine->isBuiltinQuery($default_filter)) { 23 + continue; 24 + } 25 + 26 + $saved = id(new PhabricatorSavedQueryQuery()) 27 + ->setViewer($viewer) 28 + ->withQueryKeys(array($default_filter)) 29 + ->executeOne(); 30 + if ($saved) { 31 + continue; 32 + } 33 + 34 + $properties = $project->getProperties(); 35 + unset($properties['workboard.filter.default']); 36 + 37 + queryfx( 38 + $conn, 39 + 'UPDATE %T SET properties = %s WHERE id = %d', 40 + $table->getTableName(), 41 + phutil_json_encode($properties), 42 + $project->getID()); 43 + 44 + echo tsprintf( 45 + "%s\n", 46 + pht( 47 + 'Project ("%s") had an invalid query saved as a default workboard '. 48 + 'query. The query has been reset. See T13208.', 49 + $project->getDisplayName())); 50 + }
+1 -1
src/applications/project/controller/PhabricatorProjectBoardViewController.php
··· 203 203 // with the column filter. If the user currently has constraints on the 204 204 // board, we want to add a new column or project constraint, not 205 205 // completely replace the constraints. 206 - $saved_query = clone $saved; 206 + $saved_query = $saved->newCopy(); 207 207 208 208 if ($query_column->getProxyPHID()) { 209 209 $project_phids = $saved_query->getParameter('projectPHIDs');
+8
src/applications/search/engine/PhabricatorApplicationSearchEngine.php
··· 103 103 } 104 104 105 105 public function saveQuery(PhabricatorSavedQuery $query) { 106 + if ($query->getID()) { 107 + throw new Exception( 108 + pht( 109 + 'Query (with ID "%s") has already been saved. Queries are '. 110 + 'immutable once saved.', 111 + $query->getID())); 112 + } 113 + 106 114 $query->setEngineClassName(get_class($this)); 107 115 108 116 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+7
src/applications/search/storage/PhabricatorSavedQuery.php
··· 63 63 return $this->assertAttachedKey($this->parameterMap, $key); 64 64 } 65 65 66 + public function newCopy() { 67 + return id(new self()) 68 + ->setParameters($this->getParameters()) 69 + ->setQueryKey(null) 70 + ->setEngineClassName($this->getEngineClassName()); 71 + } 72 + 66 73 67 74 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 68 75