You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1082 lines
28 KiB

  1. <?php
  2. import("classes.BaseController");
  3. /**
  4. * collection controller
  5. *
  6. * You should always input these parameters:
  7. * - db
  8. * - collection
  9. * to call the actions.
  10. *
  11. * @author iwind
  12. *
  13. */
  14. class CollectionController extends BaseController {
  15. /**
  16. * DB Name
  17. *
  18. * @var string
  19. */
  20. public $db;
  21. /**
  22. * Collection Name
  23. *
  24. * @var string
  25. */
  26. public $collection;
  27. /**
  28. * DB instance
  29. *
  30. * @var MongoDB
  31. */
  32. protected $_mongodb;
  33. public function onBefore() {
  34. parent::onBefore();
  35. $this->db = xn("db");
  36. $this->collection = xn("collection");
  37. $this->_mongodb = $this->_mongo->selectDB($this->db);
  38. }
  39. /**
  40. * load single record
  41. *
  42. */
  43. public function doRecord() {
  44. $id = rock_real_id(xn("id"));
  45. $format = xn("format");
  46. $queryFields = x("query_fields");
  47. $fields = array();
  48. if (!empty($queryFields)) {
  49. foreach ($queryFields as $queryField) {
  50. $fields[$queryField] = 1;
  51. }
  52. }
  53. $row = $this->_mongodb->selectCollection($this->collection)->findOne(array( "_id" => $id ), $fields);
  54. if (empty($row)) {
  55. $this->_outputJson(array("code" => 300, "message" => "The record has been removed."));
  56. }
  57. $exporter = new VarExportor($this->_mongodb, $row);
  58. $data = $exporter->export($format);
  59. $html = $this->_highlight($row, $format, true);
  60. $this->_outputJson(array("code" => 200, "data" => $data, "html" => $html ));
  61. }
  62. /**
  63. * switch format between array and json
  64. */
  65. public function doSwitchFormat() {
  66. $data = xn("data");
  67. $format = x("format");
  68. $ret = null;
  69. if ($format == "json") {//to json
  70. $eval = new VarEval($data, "array", $this->_mongodb);
  71. $array = $eval->execute();
  72. $exportor = new VarExportor($this->_mongodb, $array);
  73. $ret = json_unicode_to_utf8($exportor->export(MONGO_EXPORT_JSON));
  74. }
  75. else if ($format == "array") {//to array
  76. $eval = new VarEval($data, "json", $this->_mongodb);
  77. $array = $eval->execute();
  78. $exportor = new VarExportor($this->_mongodb, $array);
  79. $ret = $exportor->export(MONGO_EXPORT_PHP);
  80. }
  81. $this->_outputJson(array("code" => 200, "data" => $ret));
  82. }
  83. /** show one collection **/
  84. public function doIndex() {
  85. $this->db = xn("db");
  86. $this->collection = xn("collection");
  87. //selected format last time
  88. $this->last_format = rock_cookie("rock_format", "json");
  89. //write query to log
  90. $params = xn();
  91. if ($this->_logQuery && count($params) > 3) {//not only "action", "db" and "collection"
  92. $logDir = dirname(__ROOT__) . DS . "logs";
  93. if (!empty($params["criteria"]) && strlen(trim($params["criteria"], "{} \t\n\r")) > 0) {
  94. if (is_writable($logDir)) {
  95. $logFile = $this->_logFile($this->db, $this->collection);
  96. $fp = null;
  97. if (!is_file($logFile)) {
  98. $fp = fopen($logFile, "a+");
  99. fwrite($fp, '<?php exit("Permission Denied"); ?>' . "\n");
  100. }
  101. else {
  102. $fp = fopen($logFile, "a+");
  103. }
  104. fwrite($fp, date("Y-m-d H:i:s") . "\n" . var_export($params, true) . "\n================\n");
  105. fclose($fp);
  106. }
  107. }
  108. }
  109. //information
  110. $db = $this->_mongo->selectDB($this->db);
  111. $info = MCollection::info($db, $this->collection);
  112. $this->canAddField = !$info["capped"];
  113. //field and sort
  114. $fields = xn("field");//order fields
  115. $orders = xn("order");//order type:asc|desc
  116. if (empty($fields)) {
  117. if (!$this->_server->docsNatureOrder()) {
  118. $fields = array(
  119. ($info["capped"]) ? "\$natural": "_id", "", "", ""
  120. );
  121. }
  122. else {
  123. $fields = array();
  124. }
  125. $orders = array(
  126. "desc", "asc", "asc", "asc"
  127. );
  128. x("field", $fields);
  129. x("order", $orders);
  130. }
  131. //format
  132. $format = x("format");
  133. if (!$format) {
  134. $format = $this->last_format;
  135. x("format", $format);
  136. }
  137. //remember last format choice
  138. $this->last_format = $format;
  139. $this->_rememberFormat($format);
  140. //read fields from collection
  141. import("models.MCollection");
  142. $this->nativeFields = MCollection::fields($db, $this->collection);
  143. $this->queryFields = x("query_fields");
  144. if (!is_array($this->queryFields)) {
  145. $this->queryFields = array();
  146. }
  147. $this->indexFields = $db->selectCollection($this->collection)->getIndexInfo();
  148. $this->recordsCount = $db->selectCollection($this->collection)->count();
  149. foreach ($this->indexFields as $index => $indexField) {
  150. $this->indexFields[$index]["keystring"] = $this->_encodeJson($indexField["key"]);
  151. }
  152. $this->queryHints = x("query_hints");
  153. if (!is_array($this->queryHints)) {
  154. $this->queryHints = array();
  155. }
  156. //new obj in modification
  157. $newobj = trim(xn("newobj"));
  158. if (!$newobj) {
  159. if ($format == "array") {
  160. x("newobj", 'array(
  161. \'$set\' => array (
  162. //your attributes
  163. )
  164. )');
  165. }
  166. else {
  167. x("newobj", '{
  168. \'$set\': {
  169. //your attributes
  170. }
  171. }');
  172. }
  173. }
  174. //conditions
  175. $native = xn("criteria");
  176. $criteria = $native;
  177. if (empty($criteria)) {
  178. $criteria = array();
  179. if ($format == "array") {
  180. $native = "array(\n\t\n)";
  181. }
  182. else if ($format == "json") {
  183. $native = '{
  184. }';
  185. }
  186. x("pagesize", 10);
  187. }
  188. else {
  189. $row = null;
  190. $eval = new VarEval($criteria, $format, $db);
  191. $row = $eval->execute();
  192. if (is_object($row)) {
  193. $row = get_object_vars($row);
  194. }
  195. if (!is_array($row)) {
  196. $this->message = "Criteria must be a valid " . (($format == "json") ? "JSON object" : "array");
  197. $this->jsonLink = "#";
  198. $this->arrayLink = "#";
  199. $this->display();
  200. return;
  201. }
  202. $criteria = $row;
  203. }
  204. //remember criteria in cookie (may be replaced by query history some day)
  205. //setcookie("criteria_" . $this->db . "__" . $this->collection . "__" . $format, $native, time() + );
  206. x("criteria", $native);
  207. import("lib.mongo.RQuery");
  208. $query = new RQuery($this->_mongo, $this->db, $this->collection);
  209. $query->cond($criteria);
  210. //sort
  211. $realOrderedFields = array();
  212. $realOrderedOrders = array();
  213. foreach ($fields as $index => $field) {
  214. if (!empty($field)) {
  215. $realOrderedFields[] = $field;
  216. if ($orders[$index] == "asc") {
  217. $realOrderedOrders[] = "asc";
  218. $query->asc($field);
  219. }
  220. else {
  221. $realOrderedOrders[] = "desc";
  222. $query->desc($field);
  223. }
  224. }
  225. }
  226. x("field", $realOrderedFields);
  227. x("order", $realOrderedOrders);
  228. //command
  229. $command = x("command");
  230. if (!$command) {
  231. $command = "findAll";
  232. x("command", $command);
  233. }
  234. $limit = xi("limit");
  235. if ($limit > 0) {
  236. $query->limit($limit);
  237. }
  238. $count = ($limit > 0 && $command == "findAll") ? $query->count(true) : $query->count();
  239. switch ($command) {
  240. case "findAll":
  241. if (!empty($this->queryFields)) {
  242. $query->result($this->queryFields);
  243. }
  244. if (!empty($this->queryHints)) {
  245. foreach ($this->indexFields as $index) {
  246. if (in_array($index["name"], $this->queryHints)) {
  247. $query->hint($index["key"]);
  248. }
  249. }
  250. }
  251. break;
  252. case "remove":
  253. $microtime = microtime(true);
  254. $query->delete();
  255. $this->cost = microtime(true) - $microtime;
  256. break;
  257. case "modify":
  258. $microtime = microtime(true);
  259. $row = null;
  260. $newobj = xn("newobj");
  261. $eval = new VarEval($newobj, $format, $db);
  262. $row = $eval->execute();
  263. if (is_array($row)) {
  264. $query->upsert($row);
  265. }
  266. $this->cost = microtime(true) - $microtime;
  267. break;
  268. }
  269. //construct links
  270. if ($format == "json") {
  271. $params = xn();
  272. unset($params["newobj"]);
  273. $exportor = new VarExportor($db, $criteria);
  274. $params["format"] = "array";
  275. $params["criteria"] = $exportor->export();
  276. $this->arrayLink = $this->path($this->action(), $params);
  277. $params = xn();
  278. unset($params["newobj"]);
  279. $params["format"] = "json";
  280. $this->jsonLink = $this->path($this->action(), $params);
  281. }
  282. else if ($format == "array") {
  283. $params = xn();
  284. unset($params["newobj"]);
  285. $params["format"] = "array";
  286. $this->arrayLink = $this->path($this->action(), $params);
  287. $params = xn();
  288. unset($params["newobj"]);
  289. $params["format"] = "json";
  290. if (empty($criteria)) {
  291. $params["criteria"] = "{\n \n}";
  292. }
  293. else {
  294. $exportor = new VarExportor($db, $criteria);
  295. $params["criteria"] = $exportor->export(MONGO_EXPORT_JSON);
  296. }
  297. $this->jsonLink = $this->path($this->action(), $params);
  298. }
  299. if ($command != "findAll") {
  300. $this->count = $count;
  301. $this->display();
  302. return;
  303. }
  304. //pagination
  305. $pagesize = xi("pagesize");
  306. if ($pagesize < 1) {
  307. $pagesize = 10;
  308. }
  309. import("lib.page.RPageStyle1");
  310. $page = new RPageStyle1();
  311. $page->setTotal($count);
  312. $page->setSize($pagesize);
  313. $page->setAutoQuery();
  314. $this->page = $page;
  315. $query->offset($page->offset());
  316. if ($limit > 0) {
  317. $query->limit(min($limit, $page->size()));
  318. }
  319. else {
  320. $query->limit($page->size());
  321. }
  322. $microtime = microtime(true);
  323. $this->rows = $query->findAll(true);
  324. $this->cost = microtime(true) - $microtime;
  325. foreach ($this->rows as $index => $row) {
  326. $native = $row;
  327. $exportor = new VarExportor($query->db(), $native);
  328. $row["text"] = $exportor->export($format);
  329. $row["data"] = $this->_highlight($native, $format, true);
  330. $row["can_delete"] = (isset($row["_id"]) && !$info["capped"]);
  331. $row["can_modify"] = isset($row["_id"]);
  332. $row["can_duplicate"] = isset($row["_id"]);
  333. $row["can_add_field"] = (isset($row["_id"]) && !$info["capped"]);
  334. $row["can_refresh"] = isset($row["_id"]);
  335. $this->rows[$index] = $row;
  336. }
  337. $this->display();
  338. }
  339. /**
  340. * output query history
  341. *
  342. */
  343. public function doQueryHistory() {
  344. ob_clean();
  345. $logs = array();
  346. $criterias = array();
  347. $this->error = null;
  348. if ($this->_logQuery) {
  349. $logFile = $this->_logFile(xn("db"), xn("collection"));
  350. $this->logs = array();
  351. if (is_file($logFile)) {
  352. $size = 10240;
  353. $fp = fopen($logFile, "r");
  354. fseek($fp, -$size, SEEK_END);
  355. $text = fread($fp, $size);
  356. fclose($fp);
  357. preg_match_all("/(\\d+\\-\\d+\\-\\d+\\s+\\d+:\\d+:\\d+)\n(.+)(={10,})/sU", $text, $match);
  358. foreach ($match[1] as $k => $time) {
  359. $eval = new VarEval($match[2][$k]);
  360. $params = $eval->execute();
  361. if (!in_array($params["criteria"], $criterias)) {
  362. $logs[] = array(
  363. "time" => $time,
  364. "params" => $params,
  365. "query" => http_build_query($params)
  366. );
  367. $criterias[] = $params["criteria"];
  368. }
  369. }
  370. if (!is_writeable($logFile)) {
  371. $this->error = "To use log_query feature, please make file '{$logFile}' writeable.";
  372. }
  373. }
  374. else {
  375. $dirname = dirname($logFile);
  376. if (!is_writeable($dirname)) {
  377. $this->error = "To use log_query feature, please make directory '{$dirname}' writeable.";
  378. }
  379. }
  380. }
  381. $this->logs = array_slice(array_reverse($logs), 0, 10);
  382. $this->display();
  383. exit();
  384. }
  385. /**
  386. * Clear query history
  387. */
  388. public function doClearQueryHistory() {
  389. if ($this->_logQuery) {
  390. $logFile = $this->_logFile(xn("db"), xn("collection"));
  391. if (is_file($logFile)) {
  392. if (@unlink($logFile)) {
  393. $this->_outputJson(array(
  394. "code" => 200
  395. ));
  396. }
  397. else {
  398. $this->_outputJson(array(
  399. "code" => 501
  400. ));
  401. }
  402. }
  403. }
  404. $this->_outputJson(array(
  405. "code" => 200
  406. ));
  407. }
  408. /** explain query **/
  409. public function doExplainQuery() {
  410. $this->db = x("db");
  411. $this->collection = xn("collection");
  412. //field and sort
  413. $fields = xn("field");
  414. $orders = xn("order");
  415. $format = xn("format");
  416. if (empty($fields)) {
  417. $fields = array(
  418. "_id", "", "", ""
  419. );
  420. $orders = array(
  421. "desc", "asc", "asc", "asc"
  422. );
  423. x("field", $fields);
  424. x("order", $orders);
  425. }
  426. //conditions
  427. $native = xn("criteria");
  428. $criteria = $native;
  429. if (empty($criteria)) {
  430. $criteria = array();
  431. $native = "array(\n\t\n)";
  432. }
  433. else {
  434. $row = null;
  435. $eval = new VarEval($criteria, $format, $this->_mongo->selectDB($this->db));
  436. $row = $eval->execute();
  437. if (!is_array($row)) {
  438. $this->error = "To explain a query, criteria must be an array.";
  439. $this->display();
  440. return;
  441. }
  442. $criteria = $row;
  443. }
  444. x("criteria", $native);
  445. import("lib.mongo.RQuery");
  446. $query = new RQuery($this->_mongo, $this->db, $this->collection);
  447. $query->cond($criteria);
  448. //sort
  449. foreach ($fields as $index => $field) {
  450. if (!empty($field)) {
  451. if ($orders[$index] == "asc") {
  452. $query->asc($field);
  453. }
  454. else {
  455. $query->desc($field);
  456. }
  457. }
  458. }
  459. //command
  460. $command = x("command");
  461. if (!$command) {
  462. $command = "findAll";
  463. x("command", $command);
  464. }
  465. $queryHints = x("query_hints");
  466. if (!empty($queryHints)) {
  467. $db = $this->_mongo->selectDB($this->db);
  468. $indexes = $db->selectCollection($this->collection)->getIndexInfo();
  469. foreach ($indexes as $index) {
  470. if (in_array($index["name"], $queryHints)) {
  471. $query->hint($index["key"]);
  472. }
  473. }
  474. }
  475. $cursor = $query->cursor();
  476. $this->ret = $this->_highlight($cursor->explain(), "json");
  477. $this->display();
  478. }
  479. /** delete on row **/
  480. public function doDeleteRow() {
  481. $this->db = x("db");
  482. $this->collection = xn("collection");
  483. import("lib.mongo.RQuery");
  484. $query = new RQuery($this->_mongo, $this->db, $this->collection);
  485. $ret = $query->id(rock_real_id(x("id")))->delete();
  486. $this->redirectUrl(xn("uri"), true);
  487. }
  488. /** create row **/
  489. public function doCreateRow() {
  490. $this->db = x("db");
  491. $this->collection = xn("collection");
  492. $id = rock_real_id(x("id"));
  493. import("lib.mongo.RQuery");
  494. //selected format last time
  495. $this->last_format = rock_cookie("rock_format", "json");
  496. //if is duplicating ...
  497. if (!$this->isPost() && $id) {
  498. $query = new RQuery($this->_mongo, $this->db, $this->collection);
  499. $row = $query->id($id)->findOne();
  500. if (!empty($row)) {
  501. unset($row["_id"]);
  502. import("classes.VarExportor");
  503. $export = new VarExportor($query->db(), $row);
  504. x("data", $export->export($this->last_format));
  505. }
  506. }
  507. //initialize
  508. if (!$this->isPost() && !x("data")) {
  509. x("data", ($this->last_format == "json") ? "{\n\t\n}" : "array(\n\n)");
  510. }
  511. //try to deal with data
  512. if ($this->isPost()) {
  513. $format = x("format");
  514. $count = xi("count");
  515. $this->last_format = $format;
  516. $data = xn("data");
  517. $data = str_replace(array(
  518. "%{created_at}"
  519. ), time(), $data);
  520. $row = null;
  521. $eval = new VarEval($data, $format, $this->_mongo->selectDb($this->db));
  522. $row = $eval->execute();
  523. if ($row === false || !is_array($row)) {
  524. $this->error = "Data must be a valid {$format}.";
  525. $this->display();
  526. return;
  527. }
  528. $query = new RQuery($this->_mongo, $this->db, $this->collection);
  529. try {
  530. for ($i = 1; $i <= $count; $i++) {
  531. $query->insert($row);
  532. unset($row['_id']);
  533. }
  534. } catch (Exception $e) {
  535. $this->error = $e->getMessage();
  536. $this->display();
  537. return;
  538. }
  539. //remember format choice
  540. $this->_rememberFormat($format);
  541. $this->redirect("collection.index", array(
  542. "db" => $this->db,
  543. "collection" => $this->collection
  544. ), true);
  545. }
  546. $this->display();
  547. }
  548. /** modify one row **/
  549. public function doModifyRow() {
  550. $this->db = xn("db");
  551. $this->collection = xn("collection");
  552. $id = rock_real_id(xn("id"));
  553. //selected format last time
  554. $this->last_format = rock_cookie("rock_format", "json");
  555. import("lib.mongo.RQuery");
  556. $query = new RQuery($this->_mongo, $this->db, $this->collection);
  557. $this->row = $query->id($id)->findOne();
  558. if (empty($this->row)) {
  559. $this->error = "Record is not found.";
  560. $this->display();
  561. return;
  562. }
  563. $this->data = $this->row;
  564. unset($this->data["_id"]);
  565. import("classes.VarExportor");
  566. $export = new VarExportor($query->db(), $this->data);
  567. $this->data = $export->export($this->last_format);
  568. if ($this->last_format == "json") {
  569. $this->data = json_unicode_to_utf8($this->data);
  570. }
  571. if ($this->isPost()) {
  572. $this->data = xn("data");
  573. $format = x("format");
  574. $this->last_format = $format;
  575. $row = null;
  576. $eval = new VarEval($this->data, $format, $this->_mongo->selectDb($this->db));
  577. $row = $eval->execute();
  578. if ($row === false || !is_array($row)) {
  579. $this->error = "Only valid {$format} is accepted.";
  580. $this->display();
  581. return;
  582. }
  583. $query = new RQuery($this->_mongo, $this->db, $this->collection);
  584. $obj = $query->id($this->row["_id"])->find();
  585. $oldAttrs = $obj->attrs();
  586. $obj->setAttrs($row);
  587. foreach ($oldAttrs as $oldAttr => $oldValue) {
  588. if ($oldAttr == "_id") {
  589. continue;
  590. }
  591. if (!array_key_exists($oldAttr, $row)) {
  592. $obj->remove($oldAttr);
  593. }
  594. }
  595. try {
  596. $obj->save();
  597. } catch (Exception $e) {
  598. $this->error = $e->getMessage();
  599. $this->display();
  600. return;
  601. }
  602. //remember format choice
  603. $this->_rememberFormat($format);
  604. $this->message = "Updated successfully.";
  605. }
  606. $this->display();
  607. }
  608. /** download file in GridFS **/
  609. public function doDownloadFile() {
  610. $this->db = xn("db");
  611. $this->collection = xn("collection");
  612. $this->id = xn("id");
  613. $db = $this->_mongo->selectDB($this->db);
  614. $prefix = substr($this->collection, 0, strrpos($this->collection, "."));
  615. $file = $db->getGridFS($prefix)->findOne(array("_id" => rock_real_id($this->id)));
  616. $fileinfo = pathinfo($file->getFilename());
  617. $extension = strtolower($fileinfo["extension"]);
  618. import("lib.mime.types", false);
  619. ob_end_clean();
  620. if (isset($GLOBALS["mime_types"][$extension])) {
  621. header("Content-Type:" . $GLOBALS["mime_types"][$extension]);
  622. }
  623. else {
  624. header("Content-Type:text/plain");
  625. }
  626. header("Content-Disposition: attachment; filename=" . $fileinfo["basename"]);
  627. header("Content-Length:" . $file->getSize());
  628. echo $file->getBytes();
  629. exit;
  630. }
  631. /** clear rows in collection **/
  632. public function doClearRows() {
  633. $this->db = x("db");
  634. $this->collection = xn("collection");
  635. import("lib.mongo.RQuery");
  636. $query = new RQuery($this->_mongo, $this->db, $this->collection);
  637. $query->delete();
  638. echo '<script language="javascript">
  639. window.parent.frames["left"].location.reload();
  640. </script>';
  641. $this->redirect("collection.index", array(
  642. "db" => $this->db,
  643. "collection" => $this->collection
  644. ), true);
  645. }
  646. /** drop collection **/
  647. public function doRemoveCollection() {
  648. $this->db = x("db");
  649. $this->collection = xn("collection");
  650. $db = $this->_mongo->selectDB($this->db);
  651. $db->dropCollection($this->collection);
  652. $this->display();
  653. }
  654. /** list collection indexes **/
  655. public function doCollectionIndexes() {
  656. $this->db = x("db");
  657. $this->collection = xn("collection");
  658. $collection = $this->_mongo->selectCollection($this->_mongo->selectDB($this->db), $this->collection);
  659. $this->indexes = $collection->getIndexInfo();
  660. foreach ($this->indexes as $_index => $index) {
  661. $index["data"] = $this->_highlight($index["key"], "json");
  662. $this->indexes[$_index] = $index;
  663. }
  664. $this->display();
  665. }
  666. /** drop a collection index **/
  667. public function doDeleteIndex() {
  668. $this->db = x("db");
  669. $this->collection = xn("collection");
  670. $db = $this->_mongo->selectDB($this->db);
  671. $collection = $this->_mongo->selectCollection($db, $this->collection);
  672. $indexes = $collection->getIndexInfo();
  673. foreach ($indexes as $index) {
  674. if ($index["name"] == trim(xn("index"))) {
  675. $ret = $db->command(array("deleteIndexes" => $collection->getName(), "index" => $index["name"]));
  676. break;
  677. }
  678. }
  679. $this->redirect("collection.collectionIndexes", array(
  680. "db" => $this->db,
  681. "collection" => $this->collection
  682. ));
  683. }
  684. /** create a collection index **/
  685. public function doCreateIndex() {
  686. $this->db = x("db");
  687. $this->collection = xn("collection");
  688. $this->nativeFields = MCollection::fields($this->_mongo->selectDB($this->db), $this->collection);
  689. if ($this->isPost()) {
  690. $db = $this->_mongo->selectDB($this->db);
  691. $collection = $this->_mongo->selectCollection($db, $this->collection);
  692. $fields = xn("field");
  693. if (!is_array($fields)) {
  694. $this->message = "Index contains one field at least.";
  695. $this->display();
  696. return;
  697. }
  698. $orders = xn("order");
  699. $attrs = array();
  700. foreach ($fields as $index => $field) {
  701. $field = trim($field);
  702. if (!empty($field)) {
  703. $attrs[$field] = ($orders[$index] == "asc") ? 1 : -1;
  704. }
  705. }
  706. if (empty($attrs)) {
  707. $this->message = "Index contains one field at least.";
  708. $this->display();
  709. return;
  710. }
  711. //if is unique
  712. $options = array();
  713. if (x("is_unique")) {
  714. $options["unique"] = 1;
  715. if (x("drop_duplicate")) {
  716. $options["dropDups"] = 1;
  717. }
  718. }
  719. $options["background"] = 1;
  720. $options["safe"] = 1;
  721. //name
  722. $name = trim(xn("name"));
  723. if (!empty($name)) {
  724. $options["name"] = $name;
  725. }
  726. $collection->ensureIndex($attrs, $options);
  727. $this->redirect("collection.collectionIndexes", array(
  728. "db" => $this->db,
  729. "collection" => $this->collection
  730. ));
  731. }
  732. $this->display();
  733. }
  734. /** create a collection index **/
  735. public function doCreate2DIndex() {
  736. $this->db = x("db");
  737. $this->collection = xn("collection");
  738. $this->nativeFields = MCollection::fields($this->_mongo->selectDB($this->db), $this->collection);
  739. if ($this->isPost()) {
  740. $db = $this->_mongo->selectDB($this->db);
  741. $collection = $this->_mongo->selectCollection($db, $this->collection);
  742. $attrs = array();
  743. //Location Field
  744. $locationField = trim(xn("location_field"));
  745. $attrs[$locationField] = "2d";
  746. //Other Fields
  747. $fields = xn("field");
  748. if (!is_array($fields)) {
  749. $this->message = "Index contains one field at least.";
  750. $this->display();
  751. return;
  752. }
  753. $orders = xn("order");
  754. foreach ($fields as $index => $field) {
  755. $field = trim($field);
  756. if (!empty($field)) {
  757. $attrs[$field] = ($orders[$index] == "asc") ? 1 : -1;
  758. }
  759. }
  760. if (empty($attrs)) {
  761. $this->message = "Index contains one field at least.";
  762. $this->display();
  763. return;
  764. }
  765. //Options
  766. $options = array();
  767. $minBound = x("min_bound");
  768. if (is_numeric($minBound)) {
  769. $minBound = floatval($minBound);
  770. $options["min"] = $minBound;
  771. }
  772. $maxBound = x("max_bound");
  773. if (is_numeric($maxBound)) {
  774. $maxBound = floatval($maxBound);
  775. $options["max"] = $maxBound;
  776. }
  777. $bits = x("bits");
  778. if (is_numeric($bits)) {
  779. $bits = intval($bits);
  780. $options["bits"] = $bits;
  781. }
  782. //name
  783. $name = trim(xn("name"));
  784. if (!empty($name)) {
  785. $options["name"] = $name;
  786. }
  787. $collection->ensureIndex($attrs, $options);
  788. $this->redirect("collection.collectionIndexes", array(
  789. "db" => $this->db,
  790. "collection" => $this->collection
  791. ));
  792. }
  793. $this->display();
  794. }
  795. /** collection statistics **/
  796. public function doCollectionStats() {
  797. $this->db = x("db");
  798. $this->collection = xn("collection");
  799. $this->stats = array();
  800. $db = $this->_mongo->selectDB($this->db);
  801. $ret = $db->command(array( "collStats" => $this->collection ));
  802. if ($ret["ok"]) {
  803. $this->stats = $ret;
  804. foreach ($this->stats as $index => $stat) {
  805. if (is_array($stat) || is_bool($stat)) {
  806. $this->stats[$index] = $this->_highlight($stat, "json");
  807. }
  808. }
  809. }
  810. //top
  811. $ret = $this->_mongo->selectDB("admin")->command(array( "top" => 1 ));
  812. $this->top = array();
  813. $namespace = $this->db . "." . $this->collection;
  814. if ($ret["ok"] && !empty($ret["totals"][$namespace])) {
  815. $this->top = $ret["totals"][$namespace];
  816. foreach ($this->top as $index => $value) {
  817. $this->top[$index] = $value["count"];
  818. }
  819. }
  820. $this->display();
  821. }
  822. /** validate collection **/
  823. public function doCollectionValidate() {
  824. $this->db = x("db");
  825. $this->collection = xn("collection");
  826. $db = $this->_mongo->selectDB($this->db);
  827. $this->ret = $this->_highlight($db->selectCollection($this->collection)->validate(), "json");
  828. $this->display();
  829. }
  830. /** rename collection **/
  831. public function doCollectionRename() {
  832. $this->db = xn("db");
  833. $this->collection = xn("collection");
  834. $this->realName = $this->collection;
  835. if ($this->isPost()) {
  836. $oldname = trim(xn("oldname"));
  837. $newname = trim(xn("newname"));
  838. $removeExists = trim(xn("remove_exists"));
  839. if ($newname === "") {
  840. $this->error = "Please enter a new name.";
  841. $this->display();
  842. return;
  843. }
  844. if (!$removeExists) {
  845. //Is there a same name?
  846. $collections = MDb::listCollections($this->_mongo->selectDB($this->db));
  847. foreach ($collections as $collection) {
  848. if ($collection->getName() == $newname) {
  849. $this->error = "There is already a '{$newname}' collection, you should drop it before renaming.";
  850. $this->display();
  851. return;
  852. }
  853. }
  854. }
  855. $this->ret = $this->_mongo->selectDB($this->db)->execute('function (coll, newname, dropExists) { db.getCollection(coll).renameCollection(newname, dropExists);}', array( $oldname, $newname, (bool)$removeExists ));
  856. if ($this->ret["ok"]) {
  857. $this->realName = $newname;
  858. $this->message = "Operation success. <a href=\"?action=collection.index&db={$this->db}&collection={$newname}\">[GO &raquo;]</a>";
  859. }
  860. else {
  861. $this->error = "Operation failure";
  862. }
  863. $this->ret_json = $this->_highlight($this->ret, "json");
  864. }
  865. $this->display();
  866. }
  867. /** collection properties **/
  868. public function doCollectionProps() {
  869. $this->db = xn("db");
  870. $this->collection = xn("collection");
  871. $ret = $this->_mongo->selectDB($this->db)->selectCollection("system.namespaces")->findOne(array(
  872. "name" => $this->db . "." . $this->collection
  873. ));
  874. $this->isCapped = 0;
  875. $this->size = 0;
  876. $this->max = 0;
  877. if (isset($ret["options"]["capped"])) {
  878. $this->isCapped = $ret["options"]["capped"];
  879. }
  880. if (isset($ret["options"]["size"])) {
  881. $this->size = $ret["options"]["size"];
  882. }
  883. if (isset($ret["options"]["max"])) {
  884. $this->max = $ret["options"]["max"];
  885. }
  886. if ($this->isPost()) {
  887. $this->isCapped = xi("is_capped");
  888. $this->size = xi("size");
  889. $this->max = xi("max");
  890. //rename current collection
  891. $bkCollection = $this->collection . "_rockmongo_bk_" . uniqid();
  892. $this->ret = $this->_mongo->selectDB($this->db)->execute('function (coll, newname, dropExists) { db.getCollection(coll).renameCollection(newname, dropExists);}', array( $this->collection, $bkCollection, true ));
  893. if (!$this->ret["ok"]) {
  894. $this->error = "There is something wrong:<font color=\"red\">{$this->ret['errmsg']}</font>, please refresh the page to try again.";
  895. $this->display();
  896. return;
  897. }
  898. //create new collection
  899. $db = $this->_mongo->selectDB($this->db);
  900. MCollection::createCollection($db, $this->collection, array(
  901. "capped" => $this->isCapped,
  902. "size" => $this->size,
  903. "max" => $this->max
  904. ));
  905. //copy data to new collection
  906. if (!$this->_copyCollection($db, $bkCollection, $this->collection, true)) {
  907. //try to recover
  908. $this->ret = $db->execute('function (coll, newname, dropExists) { db.getCollection(coll).renameCollection(newname, dropExists);}', array( $bkCollection, $this->collection, true ));
  909. $this->error = "There is something wrong:<font color=\"red\">{$ret['errmsg']}</font>, please refresh the page to try again.";
  910. $this->display();
  911. return;
  912. }
  913. //drop current collection
  914. $db->dropCollection($bkCollection);
  915. }
  916. $this->display();
  917. }
  918. /** duplicate collection **/
  919. public function doCollectionDuplicate() {
  920. $this->db = xn("db");
  921. $this->collection = xn("collection");
  922. if (!$this->isPost()) {
  923. x("target", $this->collection . "_copy");
  924. x("remove_target", 1);
  925. x("copy_indexes", 1);
  926. }
  927. if ($this->isPost()) {
  928. $target = trim(xn("target"));
  929. $removeTarget = x("remove_target");
  930. $copyIndexes = x("copy_indexes");
  931. if ($target === "") {
  932. $this->error = "Please enter a valid target.";
  933. $this->display();
  934. return;
  935. }
  936. $db = $this->_mongo->selectDB($this->db);
  937. if ($removeTarget) {
  938. $db->selectCollection($target)->drop();
  939. }
  940. $this->_copyCollection($db, $this->collection, $target, $copyIndexes);
  941. $this->message = "Collection duplicated successfully.";
  942. }
  943. $this->display();
  944. }
  945. /** transfer a collection **/
  946. public function doCollectionTransfer() {
  947. $this->redirect("db.dbTransfer", array(
  948. "db" => xn("db"),
  949. "collection" => xn("collection")
  950. ));
  951. }
  952. /** export a collection **/
  953. public function doCollectionExport() {
  954. $this->redirect("db.dbExport", array( "db" => xn("db"), "collection" => xn("collection"), "can_download" => xn("can_download") ));
  955. }
  956. /** import a collection **/
  957. public function doCollectionImport() {
  958. $this->redirect("db.dbImport", array( "db" => xn("db") ));
  959. }
  960. }
  961. ?>