From 88f623377869be1a380d71f425fd24cf5973cd22 Mon Sep 17 00:00:00 2001 From: Andre Lorbach Date: Mon, 21 Jul 2008 17:19:28 +0200 Subject: [PATCH] Added source admin pages and implemented listing of sources. Adding/edit and delete will be added in the next step --- src/admin/searches.php | 2 +- src/admin/sources.php | 402 +++++++++++++++++++++++++ src/admin/views.php | 4 +- src/images/icons/data_gear.png | Bin 0 -> 885 bytes src/images/icons/data_table.png | Bin 0 -> 788 bytes src/images/icons/document_text.png | Bin 0 -> 601 bytes src/include/functions_common.php | 5 +- src/include/functions_config.php | 73 ++++- src/lang/en/admin.php | 17 ++ src/templates/admin/admin_sources.html | 97 ++++++ 10 files changed, 591 insertions(+), 9 deletions(-) create mode 100644 src/admin/sources.php create mode 100644 src/images/icons/data_gear.png create mode 100644 src/images/icons/data_table.png create mode 100644 src/images/icons/document_text.png create mode 100644 src/templates/admin/admin_sources.html diff --git a/src/admin/searches.php b/src/admin/searches.php index 2d77f40..63b2f63 100644 --- a/src/admin/searches.php +++ b/src/admin/searches.php @@ -3,7 +3,7 @@ ********************************************************************* * phpLogCon - http://www.phplogcon.org * ----------------------------------------------------------------- - * User Admin File + * Search Admin File * * -> Helps administrating custom searches * diff --git a/src/admin/sources.php b/src/admin/sources.php new file mode 100644 index 0000000..85cb841 --- /dev/null +++ b/src/admin/sources.php @@ -0,0 +1,402 @@ + Helps administrating phplogcon datasources + * + * All directives are explained within this file + * + * Copyright (C) 2008 Adiscon GmbH. + * + * This file is part of phpLogCon. + * + * PhpLogCon is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PhpLogCon is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with phpLogCon. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this + * distribution + ********************************************************************* +*/ + +// *** Default includes and procedures *** // +define('IN_PHPLOGCON', true); +$gl_root_path = './../'; + +// Now include necessary include files! +include($gl_root_path . 'include/functions_common.php'); +include($gl_root_path . 'include/functions_frontendhelpers.php'); +include($gl_root_path . 'include/functions_filters.php'); + +// Set PAGE to be ADMINPAGE! +define('IS_ADMINPAGE', true); +$content['IS_ADMINPAGE'] = true; +InitPhpLogCon(); +InitSourceConfigs(); +InitFrontEndDefaults(); // Only in WebFrontEnd +InitFilterHelpers(); // Helpers for frontend filtering! + +// Init admin langauge file now! +IncludeLanguageFile( $gl_root_path . '/lang/' . $LANG . '/admin.php' ); +// --- + +// --- BEGIN Custom Code + +// Only if the user is an admin! +//if ( !isset($_SESSION['SESSION_ISADMIN']) || $_SESSION['SESSION_ISADMIN'] == 0 ) +// DieWithFriendlyErrorMsg( $content['LN_ADMIN_ERROR_NOTALLOWED'] ); + +if ( isset($_GET['op']) ) +{ + if ($_GET['op'] == "add") + { + // Set Mode to add + $content['ISEDITORNEWSEARCH'] = "true"; + $content['SEARCH_FORMACTION'] = "addnewsearch"; + $content['SEARCH_SENDBUTTON'] = $content['LN_SEARCH_ADD']; + + //PreInit these values + $content['DisplayName'] = ""; + $content['SearchQuery'] = ""; + $content['userid'] = null; + $content['CHECKED_ISUSERONLY'] = ""; + $content['SEARCHID'] = ""; + + // --- Check if groups are available + $content['SUBGROUPS'] = GetGroupsForSelectfield(); + if ( is_array($content['SUBGROUPS']) ) + $content['ISGROUPSAVAILABLE'] = true; + else + $content['ISGROUPSAVAILABLE'] = false; + + /* + $sqlquery = "SELECT " . + DB_GROUPS . ".ID as mygroupid, " . + DB_GROUPS . ".groupname " . + "FROM " . DB_GROUPS . + " ORDER BY " . DB_GROUPS . ".groupname"; + $result = DB_Query($sqlquery); + $content['SUBGROUPS'] = DB_GetAllRows($result, true); + if ( isset($content['SUBGROUPS']) && count($content['SUBGROUPS']) > 0 ) + { + // Process All Groups + for($i = 0; $i < count($content['SUBGROUPS']); $i++) + $content['SUBGROUPS'][$i]['group_selected'] = ""; + + // Enable Group Selection + $content['ISGROUPSAVAILABLE'] = true; + array_unshift( $content['SUBGROUPS'], array ("mygroupid" => -1, "groupname" => $content['LN_SEARCH_SELGROUPENABLE'], "group_selected" => "") ); + } + else + $content['ISGROUPSAVAILABLE'] = false;*/ + // --- + } + else if ($_GET['op'] == "edit") + { + // Set Mode to edit + $content['ISEDITORNEWSEARCH'] = "true"; + $content['SEARCH_FORMACTION'] = "editsearch"; + $content['SEARCH_SENDBUTTON'] = $content['LN_SEARCH_EDIT']; + + if ( isset($_GET['id']) ) + { + //PreInit these values + $content['SEARCHID'] = DB_RemoveBadChars($_GET['id']); + + $sqlquery = "SELECT * " . + " FROM " . DB_SEARCHES . + " WHERE ID = " . $content['SEARCHID']; + + $result = DB_Query($sqlquery); + $mysearch = DB_GetSingleRow($result, true); + if ( isset($mysearch['DisplayName']) ) + { + $content['SEARCHID'] = $mysearch['ID']; + $content['DisplayName'] = $mysearch['DisplayName']; + $content['SearchQuery'] = $mysearch['SearchQuery']; + if ( $mysearch['userid'] != null ) + $content['CHECKED_ISUSERONLY'] = "checked"; + else + $content['CHECKED_ISUSERONLY'] = ""; + + // --- Check if groups are available + $content['SUBGROUPS'] = GetGroupsForSelectfield(); + if ( is_array($content['SUBGROUPS']) ) + { + // Process All Groups + for($i = 0; $i < count($content['SUBGROUPS']); $i++) + { + if ( $mysearch['groupid'] != null && $content['SUBGROUPS'][$i]['mygroupid'] == $mysearch['groupid'] ) + $content['SUBGROUPS'][$i]['group_selected'] = "selected"; + else + $content['SUBGROUPS'][$i]['group_selected'] = ""; + } + + // Enable Group Selection + $content['ISGROUPSAVAILABLE'] = true; + } + else + $content['ISGROUPSAVAILABLE'] = false; + // --- +/* + // --- Check if groups are available + $sqlquery = "SELECT " . + DB_GROUPS . ".ID as mygroupid, " . + DB_GROUPS . ".groupname " . + "FROM " . DB_GROUPS . + " ORDER BY " . DB_GROUPS . ".groupname"; + $result = DB_Query($sqlquery); + $content['SUBGROUPS'] = DB_GetAllRows($result, true); + if ( isset($content['SUBGROUPS']) && count($content['SUBGROUPS']) > 0 ) + { + // Process All Groups + for($i = 0; $i < count($content['SUBGROUPS']); $i++) + { + if ( $mysearch['groupid'] != null && $content['SUBGROUPS'][$i]['mygroupid'] == $mysearch['groupid'] ) + $content['SUBGROUPS'][$i]['group_selected'] = "selected"; + else + $content['SUBGROUPS'][$i]['group_selected'] = ""; + } + + // Enable Group Selection + $content['ISGROUPSAVAILABLE'] = true; + array_unshift( $content['SUBGROUPS'], array ("mygroupid" => -1, "groupname" => $content['LN_SEARCH_SELGROUPENABLE'], "group_selected" => "") ); + } + else + $content['ISGROUPSAVAILABLE'] = false; + // --- +*/ + } + else + { + $content['ISEDITORNEWSEARCH'] = false; + $content['ISERROR'] = true; + $content['ERROR_MSG'] = GetAndReplaceLangStr( $content['LN_SEARCH_ERROR_IDNOTFOUND'], $content['SEARCHID'] ); + } + } + else + { + $content['ISEDITORNEWSEARCH'] = false; + $content['ISERROR'] = true; + $content['ERROR_MSG'] = $content['LN_SEARCH_ERROR_INVALIDID']; + } + } + else if ($_GET['op'] == "delete") + { + if ( isset($_GET['id']) ) + { + //PreInit these values + $content['SEARCHID'] = DB_RemoveBadChars($_GET['id']); + + // Get UserInfo + $result = DB_Query("SELECT DisplayName FROM " . DB_SEARCHES . " WHERE ID = " . $content['SEARCHID'] ); + $myrow = DB_GetSingleRow($result, true); + if ( !isset($myrow['DisplayName']) ) + { + $content['ISERROR'] = true; + $content['ERROR_MSG'] = GetAndReplaceLangStr( $content['LN_SEARCH_ERROR_IDNOTFOUND'], $content['SEARCHID'] ); + } + + // --- Ask for deletion first! + if ( (!isset($_GET['verify']) || $_GET['verify'] != "yes") ) + { + // This will print an additional secure check which the user needs to confirm and exit the script execution. + PrintSecureUserCheck( GetAndReplaceLangStr( $content['LN_SEARCH_WARNDELETESEARCH'], $myrow['DisplayName'] ), $content['LN_DELETEYES'], $content['LN_DELETENO'] ); + } + // --- + + // do the delete! + $result = DB_Query( "DELETE FROM " . DB_SEARCHES . " WHERE ID = " . $content['SEARCHID'] ); + if ($result == FALSE) + { + $content['ISERROR'] = true; + $content['ERROR_MSG'] = GetAndReplaceLangStr( $content['LN_SEARCH_ERROR_DELSEARCH'], $content['SEARCHID'] ); + } + else + DB_FreeQuery($result); + + // Do the final redirect + RedirectResult( GetAndReplaceLangStr( $content['LN_SEARCH_ERROR_HASBEENDEL'], $myrow['DisplayName'] ) , "searches.php" ); + } + else + { + $content['ISERROR'] = true; + $content['ERROR_MSG'] = $content['LN_SEARCH_ERROR_INVALIDID']; + } + } +} + +if ( isset($_POST['op']) ) +{ + if ( isset ($_POST['id']) ) { $content['SEARCHID'] = intval(DB_RemoveBadChars($_POST['id'])); } else {$content['SEARCHID'] = -1; } + if ( isset ($_POST['DisplayName']) ) { $content['DisplayName'] = DB_RemoveBadChars($_POST['DisplayName']); } else {$content['DisplayName'] = ""; } + if ( isset ($_POST['SearchQuery']) ) { $content['SearchQuery'] = DB_RemoveBadChars($_POST['SearchQuery']); } else {$content['SearchQuery'] = ""; } + + // User & Group handeled specially + if ( isset ($_POST['isuseronly']) ) + { + $content['userid'] = $content['SESSION_USERID']; + $content['groupid'] = "null"; // Either user or group not both! + } + else + { + $content['userid'] = "null"; + if ( isset ($_POST['groupid']) && $_POST['groupid'] != -1 ) + $content['groupid'] = intval($_POST['groupid']); + else + $content['groupid'] = "null"; + } + + // --- Check mandotary values + if ( $content['DisplayName'] == "" ) + { + $content['ISERROR'] = true; + $content['ERROR_MSG'] = $content['LN_SEARCH_ERROR_DISPLAYNAMEEMPTY']; + } + else if ( $content['SearchQuery'] == "" ) + { + $content['ISERROR'] = true; + $content['ERROR_MSG'] = $content['LN_SEARCH_ERROR_SEARCHQUERYEMPTY']; + } + // --- + + if ( !isset($content['ISERROR']) ) + { + // Everything was alright, so we go to the next step! + if ( $_POST['op'] == "addnewsearch" ) + { + // Add custom search now! + $sqlquery = "INSERT INTO " . DB_SEARCHES . " (DisplayName, SearchQuery, userid, groupid) + VALUES ('" . $content['DisplayName'] . "', + '" . $content['SearchQuery'] . "', + " . $content['userid'] . ", + " . $content['groupid'] . " + )"; + $result = DB_Query($sqlquery); + DB_FreeQuery($result); + + // Do the final redirect + RedirectResult( GetAndReplaceLangStr( $content['LN_SEARCH_HASBEENADDED'], $content['DisplayName'] ) , "searches.php" ); + } + else if ( $_POST['op'] == "editsearch" ) + { + $result = DB_Query("SELECT ID FROM " . DB_SEARCHES . " WHERE ID = " . $content['SEARCHID']); + $myrow = DB_GetSingleRow($result, true); + if ( !isset($myrow['ID']) ) + { + $content['ISERROR'] = true; + $content['ERROR_MSG'] = GetAndReplaceLangStr( $content['LN_SEARCH_ERROR_IDNOTFOUND'], $content['SEARCHID'] ); + } + else + { + // Edit the Search Entry now! + $result = DB_Query("UPDATE " . DB_SEARCHES . " SET + DisplayName = '" . $content['DisplayName'] . "', + SearchQuery = '" . $content['SearchQuery'] . "', + userid = " . $content['userid'] . ", + groupid = " . $content['groupid'] . " + WHERE ID = " . $content['SEARCHID']); + DB_FreeQuery($result); + + // Done redirect! + RedirectResult( GetAndReplaceLangStr( $content['LN_SEARCH_HASBEENEDIT'], $content['DisplayName']) , "searches.php" ); + } + } + } +} + +if ( !isset($_POST['op']) && !isset($_GET['op']) ) +{ + // Default Mode = List Searches + $content['LISTSOURCES'] = "true"; + + // Copy Views array for further modifications + $content['SOURCES'] = $content['Sources']; + + // --- Process Sources + $i = 0; // Help counter! + foreach ($content['SOURCES'] as &$mySource ) + { + // --- Set Image for Type + // NonNUMERIC are config files Sources, can not be editied + if ( is_numeric($mySource['ID']) ) + { + if ( $mySource['userid'] != null ) + { + $mySource['SourcesAssignedToImage'] = $content["MENU_ADMINUSERS"]; + $mySource['SourcesAssignedToText'] = $content["LN_GEN_USERONLY"]; + } + else if ( $mySource['groupid'] != null ) + { + $mySource['SourcesAssignedToImage'] = $content["MENU_ADMINGROUPS"]; + $mySource['SourcesAssignedToText'] = $content["LN_GEN_GROUPONLY"]; + } + else + { + $mySource['SourcesAssignedToImage'] = $content["MENU_GLOBAL"]; + $mySource['SourcesAssignedToText'] = $content["LN_GEN_GLOBAL"]; + } + } + else + { + $mySource['SourcesAssignedToImage'] = $content["MENU_INTERNAL"]; + $mySource['SourcesAssignedToText'] = $content["LN_GEN_CONFIGFILE"]; + } + // --- + + // --- Set SourceType + if ( $mySource['SourceType'] == SOURCE_DISK ) + { + $mySource['SourcesTypeImage'] = $content["MENU_SOURCE_DISK"]; + $mySource['SourcesTypeText'] = $content["LN_SOURCES_DISK"]; + } + else if ( $mySource['SourceType'] == SOURCE_DB ) + { + $mySource['SourcesTypeImage'] = $content["MENU_SOURCE_DB"]; + $mySource['SourcesTypeText'] = $content["LN_SOURCES_DB"]; + } + else if ( $mySource['SourceType'] == SOURCE_PDO ) + { + $mySource['SourcesTypeImage'] = $content["MENU_SOURCE_PDO"]; + $mySource['SourcesTypeText'] = $content["LN_SOURCES_PDO"]; + } + // --- + + // --- Set CSS Class + if ( $i % 2 == 0 ) + $mySource['cssclass'] = "line1"; + else + $mySource['cssclass'] = "line2"; + $i++; + // --- + } + // --- +// print_r ( $content['SOURCES'] ); + +} +// --- END Custom Code + +// --- BEGIN CREATE TITLE +$content['TITLE'] = InitPageTitle(); +$content['TITLE'] .= " :: " . $content['LN_ADMINMENU_SOURCEOPT']; +// --- END CREATE TITLE + +// --- Parsen and Output +InitTemplateParser(); +$page -> parser($content, "admin/admin_sources.html"); +$page -> output(); +// --- + +?> \ No newline at end of file diff --git a/src/admin/views.php b/src/admin/views.php index d333073..dd1196b 100644 --- a/src/admin/views.php +++ b/src/admin/views.php @@ -3,7 +3,7 @@ ********************************************************************* * phpLogCon - http://www.phplogcon.org * ----------------------------------------------------------------- - * User Admin File + * Views Admin File * * -> Helps administrating custom user views * @@ -543,7 +543,7 @@ if ( !isset($_POST['op']) && !isset($_GET['op']) ) // Copy Views array for further modifications $content['VIEWS'] = $content['Views']; - // --- Process Users + // --- Process Views $i = 0; // Help counter! foreach ($content['VIEWS'] as &$myView ) { diff --git a/src/images/icons/data_gear.png b/src/images/icons/data_gear.png new file mode 100644 index 0000000000000000000000000000000000000000..b18c9b3b061b3bf28642df758975866685800d9e GIT binary patch literal 885 zcmV-*1B(2KP)WdKcSAT=OOa(W;#FfcP9F*iCeF*-0cAS*C2Fffdk6>0zg00(qQO+^RR z1s4r5HI=L71^@s632;bRa{vGe@Bjb`@Bu=sG?)MY00d`2O+f$vv5yPLe) zXxyGfe4;)!OCP>vaxUmb(N&1de0bTCIAPPG^6lQs36Y;g=t9JBN^40y?!B9PD|w zuyCKQR&f=Vd0qrTkfG6#O~qoxmdzH1*VoVQ;noL;7=pPVK#~SiA^}h=mf`%I0ZEdz zaXz2Nz68-|9Q%SEx2htUb^%gt>UF+BQ2;C};YJ?HWeymI2ZKQm^Ydfi_q$+maZVMm zBMj|csSyxHqmHC$0h~?>Y&J87u7KI>0Q3T4u?)=4-hpKD)HFW+(7L_-mPQU92jbrd zSXSg&)&M6bGVJeHL9Zv_@UQ@p$T5tLIx(6C_Vii}3W@UR2P z9AvY3I5;RnFzAJ?tq4>qHE_FK;Bq-|KCPru@gAfF5Ib4~I-MwR9FKX{Fn1GVGI^Mq z@}Zc75fCsveG{Xk6;YHhvR=aL4Gbae3PP_5G#Z3tx0{eGLoQcEK?+t^H?->v28VD? z1dFAEsMj0lQw7KU*O1n$-iVrll1LoCiAHZcGMl@Jbh-eckRR)z0;LjWP)WdKcSAT=OOa(W;#FfcP9F*iCeF*-0cAS*C2Fffdk6>0zg00(qQO+^RR z1s4r5HI=L71^@s632;bRa{vGe@Bjb`@Bu=sG?)MY00d`2O+f$vv5yP`;}}{V^K7y5ZaVdg#H7)HIS=k!CTLzhtU2J9=(Zp)Kj6J6k3SA z2il7vg$gAb{7%Rw*^SxV?AP=+<0huH18--RnfK=X-poj#w!OX0qA0?@2!cTLaU4IG z+uGWCM${I@0=$JGW1|sc2M332Fks9y8Edy0+uJ*2mzRv4oiSz@jFrn}MzoKFr|RbB z@oUR6vA_Qvz8}K#d_o#KJ6|D5j37alhxkv+6LdA7pZm74QNryl;JP4kNJ25$kJ0Pd zxVh<)_$REd-!sW9?_~Z_WjUoc8lc4%Omjf>#bVs)bg1v+lml~SRq3QDCl5{E#oR=F2Qr&T$V@v*X^V1E7yMxz+* zwt|@%4VtD<{W3Y|3*y)TKb47q35jQz;#(nL1J|^N5NU*D7)Pl*=!n_Oa|3z zm9pueSX@g2RK7>W|m@oC~M7)BGz%Q|*< zKTO_1;y_68#1@Lhx9@Yg{F|kv{Bupq%=x}gw2@-`^^{g`y3@3^Z#?g zXD*-;RtT>MFSsD#QL>PVLWj^y2=0`7kH1D~TngcF0)2eXlbyU|_h|={XYmLAlcb1> S@_V}g0000WdKcYATc!{L3L*!GB7YTATcpIGch_bG$1Q5F)%RM{uz1z000McNliru z)&&<0F*%$asVx8i010qNS#tmY3h)2`3h)6!tTdPa000DMK}|sb0I`n?{9y$E00FT{ zL_t(|+Ko}aOT$nQzSpMU++C6CL0ib7_ZXrBwt9`4LD z{QsaU3H&g1Nd;pJn9rTPl6eb_uWuNeoY}+WIASn#;5aV)6(qg8p2fL%DA0tX(lu>O zARZB)vIW6?DwoS)3x=@-7Ov?w7jiGVXn^N&7F19Dnt@>$fq+aZ=!VZ8ZqPhD67nTM zHODlSKw+A)qOvZ8 0 ) + { + // Overwrite existing Views array + unset($CFG['Sources']); + + // Unpack the Columns and append to Views Array + foreach ($myrows as &$mySource ) + { + // Append to Views Array + $CFG['Sources'][ $mySource['ID'] ] = $mySource['ID']; + } + + // Copy to content array! + $content['Sources'] = $CFG['Sources']; + } } diff --git a/src/lang/en/admin.php b/src/lang/en/admin.php index 6755cac..a12040c 100644 --- a/src/lang/en/admin.php +++ b/src/lang/en/admin.php @@ -70,6 +70,7 @@ $content['LN_GEN_CUSTBTNSRCH'] = "Custom search string"; $content['LN_GEN_SUCCESSFULLYSAVED'] = "The configuration Values have been successfully saved"; $content['LN_GEN_INTERNAL'] = "Internal"; $content['LN_GEN_DISABLED'] = "Function disabled"; +$content['LN_GEN_CONFIGFILE'] = "Configuration File"; $content['LN_GEN_'] = ""; // User Center @@ -178,5 +179,21 @@ $content['LN_VIEWS_HASBEENEDIT'] = "The Custom Search '%1' has been successfully $content['LN_VIEWS_'] = ""; $content['LN_VIEWS_'] = ""; +$content['LN_SOURCES_CENTER'] = "Sources Options"; +$content['LN_SOURCES_EDIT'] = "Edit Source"; +$content['LN_SOURCES_DELETE'] = "Delete Source"; +$content['LN_SOURCES_ID'] = "ID"; +$content['LN_SOURCES_NAME'] = "Source Name"; +$content['LN_SOURCES_TYPE'] = "Source Type"; +$content['LN_SOURCES_ASSIGNTO'] = "Assigned To"; +$content['LN_SOURCES_DISK'] = "Diskfile"; +$content['LN_SOURCES_DB'] = "MySQL Database"; +$content['LN_SOURCES_PDO'] = "PDO Datasource"; +$content['LN_SOURCES_ADD'] = "Add new Source"; +$content['LN_SOURCES_'] = ""; +$content['LN_SOURCES_'] = ""; +$content['LN_SOURCES_'] = ""; +$content['LN_SOURCES_'] = ""; + ?> \ No newline at end of file diff --git a/src/templates/admin/admin_sources.html b/src/templates/admin/admin_sources.html new file mode 100644 index 0000000..f0cce4d --- /dev/null +++ b/src/templates/admin/admin_sources.html @@ -0,0 +1,97 @@ + + + +
+

{ERROR_MSG}

+
+ + + + + + + + + +
{LN_SOURCES_CENTER}
+

+ + + + + + + + + + + + + + + + + + + + + + +
{LN_SOURCES_ID}{LN_SOURCES_NAME}{LN_SOURCES_TYPE}{LN_SOURCES_ASSIGNTO}{LN_GEN_ACTIONS}
{ID}{Name} {SourcesTypeText} {SourcesAssignedToText} + +   +   + + +   +   + +
 {LN_SOURCES_ADD}
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
{LN_SEARCH_ADDEDIT}
{LN_SEARCH_NAME}
{LN_GEN_USERONLY}
{LN_GEN_USERONLY}
{LN_SEARCH_GROUPONLY} + +
+ + + +
+
+ + +

+ +
+ + \ No newline at end of file